refactor(persistence): normalize UserProfile into a shared user_profiles table - #1210
Merged
Merged
Conversation
…les table The full UserProfile was serialized as a JSON blob and duplicated onto every chat_members row (once per membership) and onto blocked_users. This normalizes it into a single user_profiles table keyed by user_id_hex, joined back via @relation. - New UserProfileEntity with decomposed scalar columns (display name, phone, email); the two inherently-nested values (social accounts, profile picture) stay serialized. - chat_members / blocked_users drop user_profile_json and join the shared profile via ChatMemberWithProfile / BlockedUserWithProfile. - v25->v26 manual migration: stages each user's legacy blob once (dedup across chats, chat's full blob preferred over the blocklist's name+avatar-only one), then table-recreates both tables to drop the column. Column drops avoid ALTER TABLE DROP COLUMN for minSdk-29 SQLite compatibility. - Preserve via backfill: a one-shot pass decomposes the staged blobs; reads also fall back to parsing a not-yet-backfilled blob, so correctness never depends on timing. - Two-writer safety: chat sync writes the authoritative full profile; blocklist sync writes only name+avatar via an INSERT OR REPLACE + correlated-subquery merge that never downgrades a richer cached profile (avoids ON CONFLICT DO UPDATE, absent on minSdk-29 SQLite). Tests: migration dedup + column-drop, backfill decomposition, partial-write preservation, and the chat-member relation join.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The full
UserProfilewas serialized as a JSON blob and duplicated onto everychat_membersrow (once per membership) and ontoblocked_users. This normalizes it into a singleuser_profilestable keyed byuser_id_hex, joined back via@Relation— mirroring the existingTokenWithBalanceRelationpattern.Changes
user_profilestable with decomposed scalar columns (display_name,phone_value/verified,email_value/verified). The two inherently-nested values (social_accounts_json,profile_picture_json) stay serialized — modelling them as columns would need child tables for no query benefit.chat_members/blocked_usersdropuser_profile_json; they now join the shared profile viaChatMemberWithProfile/BlockedUserWithProfile.ALTER TABLE ... DROP COLUMNfor minSdk-29 SQLite compatibility.PersistenceProvider) decomposes the staged blobs into columns and clears the staging field. Reads also fall back to parsing a not-yet-backfilled blob, so correctness never depends on backfill timing.upsertFull); blocklist sync writes only name+avatar via anINSERT OR REPLACE+ correlated-subquery merge that never downgrades a richer cached profile (avoidsON CONFLICT DO UPDATE, which minSdk-29 SQLite lacks).Testing
:apps:flipcash:app:assembleDebuggreen. New unit tests (Robolectric) cover: migration dedup + column-drop + blob staging, backfill decomposition, partial-write-preserves-richer-profile, and the chat-member relation join. Full:db+:sourcessuites pass.