hyperswitch
hyperswitch copied to clipboard
feat(users): store and retrieve lineage_context from DB instead of Redis
Type of Change
- [ ] Bugfix
- [x] New feature
- [x] Enhancement
- [x] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD
Description
Previously, the user's lineage_context (userid, org_id, merchant_id, profile_id, role_id, tenant_id) was cached in Redis.
This PR moves the storage and retrieval of lineage_context to the PostgreSQL users table instead of Redis.
This ensures permanent persistence of lineage_context in DB, as opposed to the 7 days TTL when stored in Redis.
Key Changes
-
Schema Change:
- Added
lineage_contextas aJSONBcolumn in theuserstable.
- Added
-
During Login:
- Attempt to fetch
lineage_contextfrom DB (users.lineage_context). - If present:
- Validate the context by checking if a matching user role exists (checks both v1 and v2 user roles).
- If valid → use it for JWT generation.
- If not → fallback to default DB-based role resolution.
- If not present → fallback directly to default role resolution.
- DB update for the new lineage context is now done via
tokio::spawnto avoid blocking response.
- Attempt to fetch
-
During Org/Merchant/Profile Switch:
- After switching, the new
lineage_contextis serialized and persisted in theuserstable. - This enables restoring the last used context during the next login.
- The update is done in the background using
tokio::spawnwith success and failure logs.
- After switching, the new
-
Error Handling:
- All DB failures or deserialization errors are logged.
- Failures do not interrupt the login/switch flow — safe fallback ensures continuity.
Additional Changes
- [ ] This PR modifies the API contract
- [x] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables
Motivation and Context
By moving the lineage_context to the users table in DB:
- We ensure the context is consistently available regardless of deployment environment.
- The last used account context becomes part of the user's persistent record.
- It simplifies logic and avoids prefix mismatches across tenants.
How did you test it?
Test Case 1: Lineage Context Caching on Login
- Log in as a user with an assigned role.
- Check that
lineage_contextis stored in theuserstable (PostgreSQL). - Log out and log back in → verify that the JWT reflects the same org/merchant/profile context without role fallback.
Test Case 2: Lineage Context Update on Org/Profile Switch
- Log in as a user and switch to another org/merchant/profile.
- Confirm that the updated context is persisted in the
lineage_contextfield in the DB. - Log out and log in again → JWT should reflect the newly switched context.
Test Case 3: Fallback to Role Resolution upon clearing lineage_context in db (Tested on local)
- Manually removed
lineage_contextfrom the DB for a user. - Logged in again → verified that the system correctly falls back to the default role resolution logic.
- Confirm that the context is repopulated in the DB after login.
Test Case 4: Fallback to Role Resolution upon deleting user_role (Tested on local)
- Manually deleted
user_rolefrom the DB for a user (user_role corresponds to user invited to another account) - Tried to log in again → verified that the system correctly falls back to the default role resolution logic.
- Confirm that the context is repopulated in the DB after login.
Checklist
- [x] I formatted the code
cargo +nightly fmt --all - [x] I addressed lints thrown by
cargo clippy - [x] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible