From 5f8f09422ee3facf59c9d09f6ce9f85a6b914ff5 Mon Sep 17 00:00:00 2001 From: Daniel Holmgren Date: Wed, 31 Jan 2024 19:56:01 -0600 Subject: [PATCH] Entryway: non-transactional createAccount (#2109) make create account non transactional --- .../api/com/atproto/server/createAccount.ts | 69 ++++++++++++------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/packages/pds/src/api/com/atproto/server/createAccount.ts b/packages/pds/src/api/com/atproto/server/createAccount.ts index 1fda28e0266..77af9a180bb 100644 --- a/packages/pds/src/api/com/atproto/server/createAccount.ts +++ b/packages/pds/src/api/com/atproto/server/createAccount.ts @@ -20,6 +20,7 @@ import Database from '../../../../db' import { didDocForSession } from './util' import { getPdsEndpoint } from '../../../../pds-agents' import { isThisPds } from '../../../proxy' +import { dbLogger as log } from '../../../../logger' export default function (server: Server, ctx: AppContext) { server.com.atproto.server.createAccount({ @@ -96,19 +97,6 @@ export default function (server: Server, ctx: AppContext) { throw err } - // Generate a real did with PLC - if (plcOp && !entrywayAssignedPds) { - try { - await ctx.plcClient.sendOperation(did, plcOp) - } catch (err) { - req.log.error( - { didKey: ctx.plcRotationKey.did(), handle }, - 'failed to create did:plc', - ) - throw err - } - } - // insert invite code use if (ctx.cfg.invites.required && inviteCode) { await ensureCodeIsAvailable(dbTxn, inviteCode, true) @@ -132,6 +120,10 @@ export default function (server: Server, ctx: AppContext) { .execute() } + if (!entrywayAssignedPds) { + await repoTxn.createRepo(did, [], now) + } + const { access, refresh } = await ctx.services .auth(dbTxn) .createSession({ @@ -140,6 +132,15 @@ export default function (server: Server, ctx: AppContext) { appPasswordName: null, }) + return { + did, + pdsDid: entrywayAssignedPds?.did ?? null, + accessJwt: access, + refreshJwt: refresh, + } + }) + + try { if (entrywayAssignedPds) { const agent = ctx.pdsAgents.get(entrywayAssignedPds.host) await agent.com.atproto.server.createAccount({ @@ -149,17 +150,21 @@ export default function (server: Server, ctx: AppContext) { recoveryKey: input.body.recoveryKey, }) } else { - // Setup repo root - await repoTxn.createRepo(did, [], now) - } - - return { - did, - pdsDid: entrywayAssignedPds?.did ?? null, - accessJwt: access, - refreshJwt: refresh, + assert(plcOp) + try { + await ctx.plcClient.sendOperation(did, plcOp) + } catch (err) { + req.log.error( + { didKey: ctx.plcRotationKey.did(), handle }, + 'failed to create did:plc', + ) + throw err + } } - }) + } catch (err) { + await cleanupUncreatedAccount(ctx, did) + throw err + } const didDoc = await didDocForSession(ctx, result) @@ -496,3 +501,21 @@ const randomIndexByWeight = (weights) => { const rand = Math.random() * sum return cumulative.findIndex((item) => item >= rand) } + +const cleanupUncreatedAccount = async ( + ctx: AppContext, + did: string, + tries = 0, +) => { + if (tries > 3) return + try { + await Promise.all([ + ctx.services.account(ctx.db).deleteAccount(did), + ctx.services.record(ctx.db).deleteForActor(did), + ctx.services.repo(ctx.db).deleteRepo(did), + ]) + } catch (err) { + log.error({ err, did, tries }, 'failed to clean up partially created user') + return cleanupUncreatedAccount(ctx, did, tries + 1) + } +}