Skip to content

Commit 0995a07

Browse files
committed
Handle pulling a single pending tenant concurrently
1 parent 0913614 commit 0995a07

1 file changed

Lines changed: 36 additions & 17 deletions

File tree

src/Database/Concerns/HasPending.php

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,27 +100,46 @@ public static function pullPending(array $attributes = []): Model&Tenant
100100
*/
101101
public static function pullPendingFromPool(bool $firstOrCreate = false, array $attributes = []): ?Tenant
102102
{
103-
$tenant = DB::transaction(function () use ($attributes): ?Tenant {
104-
/** @var (Model&Tenant)|null $tenant */
105-
$tenant = static::onlyPending()->first();
106-
107-
if ($tenant !== null) {
108-
event(new PullingPendingTenant($tenant));
109-
$tenant->update(array_merge($attributes, [
110-
'pending_since' => null,
111-
]));
103+
// Attempt pulling a pending tenant.
104+
// The loop handles the case where a single tenant is being pulled by multiple processes at the same time.
105+
// If a tenant was pulled by a concurrent process, try pulling the next one in the pool.
106+
while (true) {
107+
/** @var (Model&Tenant)|null $pullCandidate */
108+
$pullCandidate = static::onlyPending()->first();
109+
110+
if ($pullCandidate === null) {
111+
return $firstOrCreate ? static::create($attributes) : null;
112112
}
113113

114-
return $tenant;
115-
});
114+
event(new PullingPendingTenant($pullCandidate));
116115

117-
if ($tenant === null) {
118-
return $firstOrCreate ? static::create($attributes) : null;
119-
}
116+
$tenant = DB::transaction(function () use ($pullCandidate, $attributes): ?Tenant {
117+
$tenantWasPulled = static::onlyPending()
118+
->whereKey($pullCandidate->getKey())
119+
->update([$pullCandidate->getColumnForQuery('pending_since') => null]) > 0;
120+
121+
if (! $tenantWasPulled) {
122+
return null;
123+
}
124+
125+
/** @var Model&Tenant $pulledTenant */
126+
$pulledTenant = static::findOrFail($pullCandidate->getKey());
127+
128+
if (! empty($attributes)) {
129+
$pulledTenant->update($attributes);
130+
}
120131

121-
// Only triggered if a tenant that was pulled from the pool is returned
122-
event(new PendingTenantPulled($tenant));
132+
return $pulledTenant;
133+
});
123134

124-
return $tenant;
135+
if ($tenant === null) {
136+
// If another pull claimed this tenant first, try claiming the next one
137+
continue;
138+
}
139+
140+
event(new PendingTenantPulled($tenant));
141+
142+
return $tenant;
143+
}
125144
}
126145
}

0 commit comments

Comments
 (0)