Skip to content

feat: LEGACY mode fallbacks for phase 2 migration#317

Open
porcellus wants to merge 10 commits intofeat/isolation-reservation-table-migrationfrom
feat/deprecate-phase-2-migration-mode
Open

feat: LEGACY mode fallbacks for phase 2 migration#317
porcellus wants to merge 10 commits intofeat/isolation-reservation-table-migrationfrom
feat/deprecate-phase-2-migration-mode

Conversation

@porcellus
Copy link
Copy Markdown
Collaborator

Summary

  • Add migration_mode config property (Ticket 15) defaulting to LEGACY
  • Wrap all write paths with MigrationMode checks (Ticket 16)
  • Wrap all read paths with MigrationMode dispatch (Ticket 17)
  • Add legacy fallbacks for account linking (checkIfLoginMethodCanBecomePrimary, checkIfLoginMethodsCanBeLinked, makePrimaryUser, linkAccounts)
  • Add legacy conflict detection for addTenantIdToPrimaryUser and reservePrimaryUserAccountInfos (bulk import)
  • Add intra-batch conflict detection for bulk import in LEGACY mode
  • Add socketTimeout=60 for JDBC connections
  • New helpers: listPrimaryUsersByEmail_legacy_forApp, listPrimaryUsersByPhoneNumber_legacy_forApp, listPrimaryUserIdsByThirdPartyInfo_legacy

Test plan

  • Full test suite: 1854/1871 passed, 2 flaky (pass in isolation), 15 skipped
  • BulkImportFlowTest: all 4 duplicate/restart tests pass
  • Account linking tests pass
  • Multitenancy tests pass
  • Email/phone/password update conflict tests pass

🤖 Generated with Claude Code

porcellus and others added 10 commits March 31, 2026 00:39
Config property with validation in validateAndNormalise(), accessor
getMigrationMode() defaulting to LEGACY. Supports env var
SUPERTOKENS_MIGRATION_MODE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add _legacy/_new variants for all migrated read methods with dispatch
based on Config.getMigrationMode().readsFromNewTables().

Files modified:
- EmailPasswordQueries: getPrimaryUserIdUsingEmail
- PasswordlessQueries: getPrimaryUserIdUsingEmail, getPrimaryUserByPhoneNumber,
  getUserInfosWithTenant_Transaction
- ThirdPartyQueries: getUserIdByThirdPartyInfo, getPrimaryUserIdUsingEmail
- WebAuthNQueries: getPrimaryUserIdForTenantUsingEmail_Transaction,
  getPrimaryUserIdForAppUsingEmail_Transaction, getPrimaryUserIdsUsingEmails_Transaction
- GeneralQueries: getUsersCount (x2), doesUserIdExist (tenant), getUsers,
  getPrimaryUserInfoForUserIds, checkIfUsesAccountLinking
- SessionQueries: getSessionInfo_Transaction, getSession

Legacy variants restored from master branch. AccountInfoQueries and
ActiveUsersQueries unchanged (new-only or not migrated).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wrap deprecated table writes with mode.writesToOldTables() and
reservation table writes with mode.writesToNewTables().
app_id_to_user_id and recipe-specific tables always written.

EmailPasswordQueries: signUp, importUsers, deleteUser, updateEmail,
  addUserIdToTenant, removeUserIdFromTenant
PasswordlessQueries: createUser, importUsers, deleteUser, updateEmail,
  updatePhoneNumber, addUserIdToTenant, removeUserIdFromTenant
ThirdPartyQueries: signUp, importUser, deleteUser, addUserIdToTenant,
  removeUserIdFromTenant
WebAuthNQueries: createUser, updateUserEmail
GeneralQueries: makePrimaryUser, linkAccounts, unlinkAccounts,
  updateTimeJoinedForPrimaryUser (+ batched variants)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…(Ticket 16)

Guard all reservation table writes in Start.java with writesToNewTables().
In LEGACY mode, skip AccountInfoQueries calls and fall through to
old-table-only behavior for makePrimaryUser and linkAccounts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Y mode

  In LEGACY mode, recipe_user_account_infos is empty, so the subquery
  to get primary_user_id returns NULL. Use the primaryUserId parameter
  directly instead.

  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Y mode

In LEGACY mode, recipe_user_account_infos is empty, so the subquery
to get primary_user_id returns NULL. Use the primaryUserId parameter
directly instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…t 17)

In LEGACY mode, these methods call per-recipe query methods
(EP, PL, TP, WebAuthn) instead of the unified AccountInfoQueries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The legacy query incorrectly referenced thirdparty_user_to_tenant.email
which doesn't exist. Fixed to use thirdparty_users.email with a 3-way
join (thirdparty_users + all_auth_recipe_users + thirdparty_user_to_tenant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multiple query paths were unconditionally reading from new tables
(recipe_user_tenants, recipe_user_account_infos, primary_user_tenants)
which are empty in LEGACY migration mode. This caused:
- Infinite loops in signInUpHelper (empty tenant IDs → retry forever)
- WrongCredentialsException on sign-in (users not found by email)
- Missing email conflict checks on email/password/phone updates
- Missing account linking conflict detection (checkIfLoginMethodCanBecomePrimary, checkIfLoginMethodsCanBeLinked)
- Missing tenant association conflict detection (addTenantIdToPrimaryUser)
- Missing bulk import conflict detection (reservePrimaryUserAccountInfos)

Key changes:
- getTenantIdsForUserIds: read from all_auth_recipe_users in LEGACY mode
- getPrimaryUserInfoForUserIds_Transaction: JOIN on old table for tenant IDs
- listPrimaryUsersByThirdPartyInfo: use thirdparty_users table in LEGACY mode
- updateUsersEmail_Transaction (EP/TP/PL/WebAuthn): add legacy email conflict check
- checkIfLoginMethodCanBecomePrimary: full legacy implementation using old tables
- checkIfLoginMethodsCanBeLinked: full legacy implementation with tenant overlap checks
- makePrimaryUser_Transaction: legacy conflict detection and already-primary check
- linkAccounts_Transaction: legacy conflict detection with resolved primary user ID
- addTenantIdToPrimaryUser_Transaction: legacy email/phone/TP conflict check
- reservePrimaryUserAccountInfos_Transaction: legacy conflict detection for bulk import
- deleteDevicesByPhoneNumber/Email_Transaction: use old table for tenant subquery
- ConnectionPool: add socketTimeout=60 for JDBC connections
- New helper methods: listPrimaryUsersByEmail_legacy_forApp, listPrimaryUsersByPhoneNumber_legacy_forApp, listPrimaryUserIdsByThirdPartyInfo_legacy

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ation

In LEGACY mode (without reservation tables), detect duplicate account
infos within the same batch of PrimaryUsers before checking against
existing users in old tables.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant