Skip to content

Commit aaf09c1

Browse files
committed
(django) Improve user and orgs sync with replica databases
1 parent ee65a0d commit aaf09c1

1 file changed

Lines changed: 20 additions & 16 deletions

File tree

  • infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/services

infrastructure/common-images/cloudharness-django/libraries/cloudharness-django/cloudharness_django/services/user.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,13 @@ def sync_kc_user(self, kc_user: ch_models.User, is_superuser=False, delete=False
111111
if not username:
112112
raise ValueError(f"Keycloak user {kc_user.id} has no username or email")
113113

114-
# Create user and member atomically
115-
user, user_created = User.objects.get_or_create(username=username)
114+
# Create user and member atomically. Pin to the primary DB so lookups
115+
# see our own uncommitted writes when a read replica is configured.
116+
user, user_created = User.objects.using("default").get_or_create(username=username)
116117

117118
# CRITICAL: Ensure Member exists before proceeding
118119
try:
119-
member = Member.objects.get(user=user)
120+
member = Member.objects.using("default").get(user=user)
120121
# Member exists but might have wrong kc_id
121122
if member.kc_id != kc_user.id:
122123
member.kc_id = kc_user.id
@@ -133,8 +134,8 @@ def sync_kc_user(self, kc_user: ch_models.User, is_superuser=False, delete=False
133134
# FINAL SAFETY CHECK: Verify Member exists before returning
134135
# This ensures we never return a user without a Member
135136
try:
136-
_ = user.member # Access member to trigger DoesNotExist if missing
137-
except:
137+
_ = Member.objects.using("default").get(user=user)
138+
except Member.DoesNotExist:
138139
# This should never happen, but if it does, create Member immediately
139140
Member.objects.create(kc_id=kc_user.id, user=user)
140141

@@ -159,9 +160,10 @@ def sync_kc_user_groups(self, kc_user: ch_models.User, user: User = None):
159160
# Sync organization memberships separately
160161
self.sync_kc_user_organizations(kc_user, user=user)
161162

162-
# Ensure the member relationship exists and is correct
163+
# Ensure the member relationship exists and is correct. Use the primary
164+
# DB so we see our own uncommitted writes when a read replica is configured.
163165
try:
164-
member = user.member
166+
member = Member.objects.using("default").get(user=user)
165167
if member.kc_id != kc_user.id:
166168
member.kc_id = kc_user.id
167169
member.save()
@@ -180,17 +182,18 @@ def sync_kc_organization(self, kc_org: ch_models.Organization) -> Organization:
180182
"""
181183
org = None
182184

183-
# First try to find by kc_id
185+
# First try to find by kc_id. Pin to the primary DB so lookups see our
186+
# own uncommitted writes when a read replica is configured.
184187
try:
185-
org = Organization.objects.get(kc_id=kc_org.id)
188+
org = Organization.objects.using("default").get(kc_id=kc_org.id)
186189
# Update name if changed
187190
if org.name != kc_org.name:
188191
org.name = kc_org.name
189192
org.save()
190193
except Organization.DoesNotExist:
191194
# Try to find by name (for organizations created without kc_id)
192195
try:
193-
org = Organization.objects.get(name=kc_org.name, kc_id__isnull=True)
196+
org = Organization.objects.using("default").get(name=kc_org.name, kc_id__isnull=True)
194197
# Update with kc_id from Keycloak
195198
org.kc_id = kc_org.id
196199
org.save()
@@ -224,14 +227,15 @@ def sync_kc_user_organizations(self, kc_user: ch_models.User, user: User = None)
224227
org = self.sync_kc_organization(kc_org)
225228
current_org_ids.add(org.id)
226229

227-
# Create membership if it doesn't exist
228-
OrganizationMember.objects.get_or_create(
229-
user=user,
230-
organization=org
231-
)
230+
# Create membership if it doesn't exist. Pin to primary so the
231+
# existence check sees writes from earlier in this transaction.
232+
try:
233+
OrganizationMember.objects.using("default").get(user=user, organization=org)
234+
except OrganizationMember.DoesNotExist:
235+
OrganizationMember.objects.create(user=user, organization=org)
232236

233237
# Remove memberships that no longer exist in Keycloak
234-
OrganizationMember.objects.filter(user=user).exclude(
238+
OrganizationMember.objects.using("default").filter(user=user).exclude(
235239
organization_id__in=current_org_ids
236240
).delete()
237241

0 commit comments

Comments
 (0)