Skip to main content
Laravel, shipping fast.

The guard above has a gap, and it's worth naming honestly: it only works if the write that records the leaf's response actually landed. If the worker dies between the leaf service confirming success and api-server persisting that fact, there's nothing in metadata to check against. The next attempt looks exactly like the first one. Left alone, it calls create again, and now there are two resources on the far end with no local record connecting either of them back to this site.

The fix isn't a better guard. It's removing the need for one, by making the resource's name itself the record.

// app/Jobs/RepositoryCreateJob.php
// Deterministic repo name derived from the site's (UUID) id, not a
// timestamp: a retry reuses the same name instead of creating a second,
// orphaned repo when a prior attempt created the repo on GitHub but the
// response was lost. The single repo's name is therefore known to the
// teardown (SiteDeactivationJob) for cleanup.
$repoName = (string) preg_replace(
    '/[^A-Za-z0-9\-]/',
    '',
    Str::of(collect(['name' => $name, 'id' => $this->site->id])->implode('-'))->lower()->replace(' ', '-')->toString()
);

Set this against the version that feels natural if you're not thinking about retries yet:

// Bad: a name that changes on every attempt
$repoName = Str::slug($name.'-'.now()->timestamp);
// retry #2, three minutes later, derives a different name and creates a
// second repository the first attempt's failure left no trace of

The timestamp version is the more obvious code to write. It's also the one where every retry is a fresh identity, free to leave behind whatever the previous attempt already built. The deterministic version seeds the name entirely from the site's own id, something that already exists, is already unique, and doesn't change between attempt one and attempt five. Retry it once, retry it ten times: the derived name is the same string every time. If the resource is already there, the leaf service's own response says so, and you treat that as success rather than failure. If it isn't there yet, you create it under the exact name the next retry, if there is one, will independently arrive at.

This is the same idea as the metadata['site']['id'] guard, aimed at the other failure mode. That guard protects you when the local write succeeds. Deterministic naming protects you when it doesn't, by making sure a retry that has completely lost track of what happened last time still can't create a duplicate. A name a system generates fresh on every attempt is a name that turns a crash into an orphan. A name a system derives from something stable turns the same crash into a no-op.