diff --git a/server/lib/rebuildClientAssociations.ts b/server/lib/rebuildClientAssociations.ts index 960106286..a9a2759c4 100644 --- a/server/lib/rebuildClientAssociations.ts +++ b/server/lib/rebuildClientAssociations.ts @@ -39,6 +39,11 @@ import { removePeerData, removeTargets as removeSubnetProxyTargets } from "@server/routers/client/targets"; +import { lockManager } from "#dynamic/lib/lock"; + +// TTL for rebuild-association locks. These functions can fan out into many +// peer/proxy updates, so give them a generous window. +const REBUILD_ASSOCIATIONS_LOCK_TTL_MS = 120000; export async function getClientSiteResourceAccess( siteResource: SiteResource, @@ -161,6 +166,23 @@ export async function rebuildClientAssociationsFromSiteResource( pubKey: string | null; subnet: string | null; }[]; +}> { + return await lockManager.withLock( + `rebuild-client-associations:site-resource:${siteResource.siteResourceId}`, + () => rebuildClientAssociationsFromSiteResourceImpl(siteResource, trx), + REBUILD_ASSOCIATIONS_LOCK_TTL_MS + ); +} + +async function rebuildClientAssociationsFromSiteResourceImpl( + siteResource: SiteResource, + trx: Transaction | typeof db = db +): Promise<{ + mergedAllClients: { + clientId: number; + pubKey: string | null; + subnet: string | null; + }[]; }> { logger.debug( `rebuildClientAssociations: [rebuildClientAssociationsFromSiteResource] START siteResourceId=${siteResource.siteResourceId} networkId=${siteResource.networkId} orgId=${siteResource.orgId}` @@ -930,6 +952,17 @@ async function handleSubnetProxyTargetUpdates( export async function rebuildClientAssociationsFromClient( client: Client, trx: Transaction | typeof db = db +): Promise { + return await lockManager.withLock( + `rebuild-client-associations:client:${client.clientId}`, + () => rebuildClientAssociationsFromClientImpl(client, trx), + REBUILD_ASSOCIATIONS_LOCK_TTL_MS + ); +} + +async function rebuildClientAssociationsFromClientImpl( + client: Client, + trx: Transaction | typeof db = db ): Promise { let newSiteResourceIds: number[] = [];