Repository navigation
feat(account): move write-lease authority into UserMeter - #1125
Conversation
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# Conflicts: # packages/worker/src/email/inbound.ts # packages/worker/src/email/outbound.ts # packages/worker/worker-configuration.d.ts Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# Conflicts: # docs/contributing/architecture/data-storage.md # packages/worker/src/account/export.node.test.ts # packages/worker/src/account/export.ts Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# Conflicts: # docs/contributing/architecture/data-storage.md Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
# Conflicts: # docs/contributing/architecture/data-storage.md Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
📝 WalkthroughWalkthroughUserMeter schema version 7 now manages non-email account write leases. D1 stores temporary mirrors. Account deletion, lease repair, listing, exports, documentation, and test environments now use the split-authority model. ChangesAccount write-lease authority
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant withAccountWriteLease
participant UserMeter
participant D1
participant ProtectedWrite
Caller->>withAccountWriteLease: request account write
withAccountWriteLease->>UserMeter: acquire DO-authoritative lease
withAccountWriteLease->>D1: create temporary mirror
withAccountWriteLease->>ProtectedWrite: execute write callback
withAccountWriteLease->>UserMeter: verify lease is held
withAccountWriteLease->>UserMeter: release lease
withAccountWriteLease->>D1: clear mirror
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
|
@coderabbitai review |
|
🔎 Preview deployed: https://kody-pr-1125.kody-a99.workers.dev Worker: Mocks:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/worker/src/entitlements/user-meter-do.ts (1)
699-716: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider running the v7 column adds whenever the stored version is not 7.
The guard
version != null && version < 7skips theALTER TABLEstatements when the meta row is absent. In that caseCREATE TABLE IF NOT EXISTSalso does not add the new columns if the table already exists, so every laterSELECT ... authority, pending_repair_idwould fail. The guard is safe today becauseinitializeSchemaalways writes the meta row. Dropping the null check makes the migration robust if that invariant changes.♻️ Proposed defensive change
- if (version != null && version < 7) { + if (version == null || version < 7) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/worker/src/entitlements/user-meter-do.ts` around lines 699 - 716, Update the migration guard around the v7 column additions to run whenever the stored version is not 7, including when version is null; preserve the existing ALTER TABLE statements and idempotent error handling in the surrounding schema initialization flow.packages/worker/src/test-support/user-meter.ts (1)
704-712: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAlign invalid-authority handling with the Durable Object.
UserMeterBase.bootstrapDeletionStatecallsassertWriteLeaseAuthority, which throws for an unrecognized authority string. This fixture instead falls back to'legacy'. A node test that passes an invalid authority would pass here and fail in production. Throw the same error to keep the fixture faithful.♻️ Proposed change
- const authority = - typeof lease.authority === 'string' && - isUserMeterWriteLeaseAuthority(lease.authority) - ? lease.authority - : 'legacy' + let authority: UserMeterWriteLeaseAuthority = 'legacy' + if (typeof lease.authority === 'string') { + if (!isUserMeterWriteLeaseAuthority(lease.authority)) { + throw new Error( + `UserMeter write lease authority must be one of do, legacy; got ${JSON.stringify(lease.authority)}.`, + ) + } + authority = lease.authority + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/worker/src/test-support/user-meter.ts` around lines 704 - 712, Update the authority handling in the lease acquisition flow around acquireWriteLease and shadowAcquireWriteLease to call the same assertWriteLeaseAuthority validation used by UserMeterBase.bootstrapDeletionState. Remove the fallback to 'legacy' for unrecognized string authorities, while preserving the existing handling for valid authorities and the legacy absence case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/worker/src/account/deletion-state.ts`:
- Around line 535-546: Update the catch block around acquireD1WriteLeaseMirror
to call releaseD1WriteLeaseMirror for the same lease before releasing the DO
write lease and rethrowing the original error. Ensure this cleanup runs for both
a false acquisition result and exceptions after a committed insert, relying on
the release method’s idempotency.
---
Nitpick comments:
In `@packages/worker/src/entitlements/user-meter-do.ts`:
- Around line 699-716: Update the migration guard around the v7 column additions
to run whenever the stored version is not 7, including when version is null;
preserve the existing ALTER TABLE statements and idempotent error handling in
the surrounding schema initialization flow.
In `@packages/worker/src/test-support/user-meter.ts`:
- Around line 704-712: Update the authority handling in the lease acquisition
flow around acquireWriteLease and shadowAcquireWriteLease to call the same
assertWriteLeaseAuthority validation used by
UserMeterBase.bootstrapDeletionState. Remove the fallback to 'legacy' for
unrecognized string authorities, while preserving the existing handling for
valid authorities and the legacy absence case.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 5f60e21f-bb4f-4aa9-8e77-56d3a0e9bf09
📒 Files selected for processing (22)
docs/contributing/account-write-lease-repair.mddocs/contributing/architecture/data-storage.mddocs/contributing/architecture/entitlements.mdpackages/worker/src/account/deletion-state.node.test.tspackages/worker/src/account/deletion-state.tspackages/worker/src/account/export.tspackages/worker/src/account/user-owned-surfaces.tspackages/worker/src/app/account-deletion.node.test.tspackages/worker/src/app/account-write-lease-middleware.node.test.tspackages/worker/src/community/avatar.node.test.tspackages/worker/src/community/community-icon.node.test.tspackages/worker/src/entitlements/user-meter-do.tspackages/worker/src/entitlements/user-meter.workers.test.tspackages/worker/src/jobs/service.node.test.tspackages/worker/src/mcp-auth.workers.test.tspackages/worker/src/mcp/capabilities/admin/admin-account-write-lease-list.tspackages/worker/src/mcp/memory/service.node.test.tspackages/worker/src/mcp/observability.workers.test.tspackages/worker/src/mcp/run-kody-registry.node.test.tspackages/worker/src/package-registry/service.node.test.tspackages/worker/src/test-support/incidental-runtime-warnings.tspackages/worker/src/test-support/user-meter.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/worker/src/account/deletion-state.node.test.ts (1)
1147-1248: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the shared D1 mirror-failure stub.
Both tests build the same
db.batchanddb.preparestubs. Only therun()behavior differs. Extract a helper that installs the stubs and returns a restore function. Call the restore function in afinallyblock so a failed assertion does not leave the stubs installed.♻️ Proposed helper
+function stubPartialMirror( + db: D1Database, + run: () => Promise<{ meta: { changes: number } }>, +) { + const originalBatch = db.batch.bind(db) + const originalPrepare = db.prepare.bind(db) + db.batch = (async () => { + throw new Error('simulated lost D1 batch response') + }) as D1Database['batch'] + db.prepare = ((query: string) => { + const statement = originalPrepare(query) + if (!query.includes('active_write_count + 1')) return statement + return { + bind(...params: Array<unknown>) { + const bound = statement.bind(...params) + return { run, first: bound.first.bind(bound), all: bound.all.bind(bound) } + }, + } + }) as D1Database['prepare'] + return () => { + db.batch = originalBatch + db.prepare = originalPrepare + } +}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/worker/src/account/deletion-state.node.test.ts` around lines 1147 - 1248, Extract the duplicated db.batch and db.prepare mocking setup from the two tests into a shared helper that accepts the differing run behavior and returns a restore function. Update both tests to use this helper, and invoke the returned restore function in finally blocks so the original database methods are restored even when assertions fail.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/worker/src/account/deletion-state.node.test.ts`:
- Around line 1147-1248: Extract the duplicated db.batch and db.prepare mocking
setup from the two tests into a shared helper that accepts the differing run
behavior and returns a restore function. Update both tests to use this helper,
and invoke the returned restore function in finally blocks so the original
database methods are restored even when assertions fail.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 749bbae3-da0e-49b3-bc19-e9e9c0830497
📒 Files selected for processing (2)
packages/worker/src/account/deletion-state.node.test.tspackages/worker/src/account/deletion-state.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/worker/src/account/deletion-state.ts
Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Summary
users.deleting_atas permanent cross-surface deletion projectionValidation
Deployment notes
High-risk rolling cutover is deployed. The temporary same-token D1 lease mirror remains until the old-isolate drain gate below passes. D1
deleting_at, the repair audit table, and email's D1 lease path remain.System recap — extends User meter into deletion-fence authority (high risk)
Mode: recap · Base:
main@e76b8c9a· Head:7e888008Classification: extends — changes authoritative coordination for non-email account write leases and deletion drains while preserving Mailbox dual-write/parity behavior.
Primitives touched
user-meterapp-uirbacaccount-exportmailboxSystem map
Non-email writes acquire in UserMeter and temporarily mirror the same token into D1; email remains on D1; deletion mark counts the deduped union.
Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).
Invariants
Conductor report
email/**diff.waitUntillease, compare active D1 tokens with UserMeter DO-authority tokens for sampled active users, allowing only known email D1-only leases. Require zero unexplained mismatches plus successful mark/list/repair smoke tests. Then remove same-token D1 acquire/release mirrors only; retain D1deleting_at, email fallback, and repair audit.env.USER_METER; cron reconciliation already passesenv. Run a complete reconciliation rotation and compareusers.d1_storage_byteswithstorageBytesShadow; require zero unexplained mismatches and cold-bootstrap/deny-at-limit smoke tests.excludeService) with UserMeter counts across active users; require zero unexplained mismatches and cold-bootstrap/restart smoke tests.Summary by CodeRabbit
Improvements
Documentation