Skip to content

Commit 8857dba

Browse files
authored
clickhouse new syncs and verify-data (#1304)
<!-- Make sure you've read the CONTRIBUTING.md guidelines: https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * External DB sync now covers teams, team members, permissions, invitations, email outbox, session replays, refresh tokens, and connected accounts. * New sequence ID fields and automatic change-flagging added to many record types to enable incremental sync. * **Improvements** * Added concurrent indexes, faster/parallelized sync pipelines, verification tooling, and richer observability. * Dashboard sequencer stats expanded and end-to-end sync tests significantly extended. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 5d9c7d9 commit 8857dba

38 files changed

Lines changed: 6074 additions & 264 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dist
5252
.docusaurus
5353
.cache-loader
5454
**.tsbuildinfo
55+
implementation.generated.ts
5556

5657
.xata*
5758

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- AlterTable
2+
ALTER TABLE "Team" ADD COLUMN "sequenceId" BIGINT,
3+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
4+
5+
-- AlterTable
6+
ALTER TABLE "TeamMember" ADD COLUMN "sequenceId" BIGINT,
7+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- AlterTable
2+
ALTER TABLE "EmailOutbox" ADD COLUMN "sequenceId" BIGINT,
3+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- AlterTable
2+
ALTER TABLE "SessionReplay" ADD COLUMN "sequenceId" BIGINT,
3+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- AlterTable
2+
ALTER TABLE "TeamMemberDirectPermission" ADD COLUMN "sequenceId" BIGINT,
3+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
4+
5+
-- AlterTable
6+
ALTER TABLE "VerificationCode" ADD COLUMN "sequenceId" BIGINT,
7+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- AlterTable
2+
ALTER TABLE "ProjectUserDirectPermission" ADD COLUMN "sequenceId" BIGINT,
3+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
4+
5+
-- AlterTable
6+
ALTER TABLE "UserNotificationPreference" ADD COLUMN "sequenceId" BIGINT,
7+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- AlterTable
2+
ALTER TABLE "ProjectUserRefreshToken" ADD COLUMN "sequenceId" BIGINT,
3+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
4+
5+
-- AlterTable
6+
ALTER TABLE "ProjectUserOAuthAccount" ADD COLUMN "sequenceId" BIGINT,
7+
ADD COLUMN "shouldUpdateSequenceId" BOOLEAN NOT NULL DEFAULT true;
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
-- Team indexes
2+
-- SPLIT_STATEMENT_SENTINEL
3+
-- SINGLE_STATEMENT_SENTINEL
4+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
5+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "Team_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."Team"("sequenceId");
6+
7+
-- SPLIT_STATEMENT_SENTINEL
8+
-- SINGLE_STATEMENT_SENTINEL
9+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
10+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "Team_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."Team"("tenancyId", "sequenceId");
11+
12+
-- SPLIT_STATEMENT_SENTINEL
13+
-- SINGLE_STATEMENT_SENTINEL
14+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
15+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "Team_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."Team"("shouldUpdateSequenceId", "tenancyId");
16+
17+
-- TeamMember indexes
18+
-- SPLIT_STATEMENT_SENTINEL
19+
-- SINGLE_STATEMENT_SENTINEL
20+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
21+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "TeamMember_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."TeamMember"("sequenceId");
22+
23+
-- SPLIT_STATEMENT_SENTINEL
24+
-- SINGLE_STATEMENT_SENTINEL
25+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
26+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "TeamMember_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."TeamMember"("tenancyId", "sequenceId");
27+
28+
-- SPLIT_STATEMENT_SENTINEL
29+
-- SINGLE_STATEMENT_SENTINEL
30+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
31+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "TeamMember_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."TeamMember"("shouldUpdateSequenceId", "tenancyId");
32+
33+
-- EmailOutbox indexes
34+
-- SPLIT_STATEMENT_SENTINEL
35+
-- SINGLE_STATEMENT_SENTINEL
36+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
37+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "EmailOutbox_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."EmailOutbox"("sequenceId");
38+
39+
-- SPLIT_STATEMENT_SENTINEL
40+
-- SINGLE_STATEMENT_SENTINEL
41+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
42+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "EmailOutbox_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."EmailOutbox"("tenancyId", "sequenceId");
43+
44+
-- SPLIT_STATEMENT_SENTINEL
45+
-- SINGLE_STATEMENT_SENTINEL
46+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
47+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "EmailOutbox_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."EmailOutbox"("shouldUpdateSequenceId", "tenancyId");
48+
49+
-- SessionReplay indexes
50+
-- SPLIT_STATEMENT_SENTINEL
51+
-- SINGLE_STATEMENT_SENTINEL
52+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
53+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "SessionReplay_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."SessionReplay"("sequenceId");
54+
55+
-- SPLIT_STATEMENT_SENTINEL
56+
-- SINGLE_STATEMENT_SENTINEL
57+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
58+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "SessionReplay_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."SessionReplay"("tenancyId", "sequenceId");
59+
60+
-- SPLIT_STATEMENT_SENTINEL
61+
-- SINGLE_STATEMENT_SENTINEL
62+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
63+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "SessionReplay_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."SessionReplay"("shouldUpdateSequenceId", "tenancyId");
64+
65+
-- TeamMemberDirectPermission indexes
66+
-- SPLIT_STATEMENT_SENTINEL
67+
-- SINGLE_STATEMENT_SENTINEL
68+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
69+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "TeamMemberDirectPermission_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."TeamMemberDirectPermission"("sequenceId");
70+
71+
-- SPLIT_STATEMENT_SENTINEL
72+
-- SINGLE_STATEMENT_SENTINEL
73+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
74+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "TeamMemberDirectPermission_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."TeamMemberDirectPermission"("shouldUpdateSequenceId", "tenancyId");
75+
76+
-- SPLIT_STATEMENT_SENTINEL
77+
-- SINGLE_STATEMENT_SENTINEL
78+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
79+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "TeamMemberDirectPermission_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."TeamMemberDirectPermission"("tenancyId", "sequenceId");
80+
81+
-- VerificationCode indexes
82+
-- SPLIT_STATEMENT_SENTINEL
83+
-- SINGLE_STATEMENT_SENTINEL
84+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
85+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "VerificationCode_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."VerificationCode"("sequenceId");
86+
87+
-- SPLIT_STATEMENT_SENTINEL
88+
-- SINGLE_STATEMENT_SENTINEL
89+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
90+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "VerificationCode_shouldUpdateSequenceId_type_idx" ON /* SCHEMA_NAME_SENTINEL */."VerificationCode"("shouldUpdateSequenceId", "type");
91+
92+
-- ProjectUserDirectPermission indexes
93+
-- SPLIT_STATEMENT_SENTINEL
94+
-- SINGLE_STATEMENT_SENTINEL
95+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
96+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserDirectPermission_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserDirectPermission"("sequenceId");
97+
98+
-- SPLIT_STATEMENT_SENTINEL
99+
-- SINGLE_STATEMENT_SENTINEL
100+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
101+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserDirectPermission_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserDirectPermission"("shouldUpdateSequenceId", "tenancyId");
102+
103+
-- SPLIT_STATEMENT_SENTINEL
104+
-- SINGLE_STATEMENT_SENTINEL
105+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
106+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserDirectPermission_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserDirectPermission"("tenancyId", "sequenceId");
107+
108+
-- UserNotificationPreference indexes
109+
-- SPLIT_STATEMENT_SENTINEL
110+
-- SINGLE_STATEMENT_SENTINEL
111+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
112+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "UserNotificationPreference_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."UserNotificationPreference"("sequenceId");
113+
114+
-- SPLIT_STATEMENT_SENTINEL
115+
-- SINGLE_STATEMENT_SENTINEL
116+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
117+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "UserNotificationPreference_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."UserNotificationPreference"("shouldUpdateSequenceId", "tenancyId");
118+
119+
-- SPLIT_STATEMENT_SENTINEL
120+
-- SINGLE_STATEMENT_SENTINEL
121+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
122+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "UserNotificationPreference_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."UserNotificationPreference"("tenancyId", "sequenceId");
123+
124+
-- ProjectUserRefreshToken indexes
125+
-- SPLIT_STATEMENT_SENTINEL
126+
-- SINGLE_STATEMENT_SENTINEL
127+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
128+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserRefreshToken_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserRefreshToken"("sequenceId");
129+
130+
-- SPLIT_STATEMENT_SENTINEL
131+
-- SINGLE_STATEMENT_SENTINEL
132+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
133+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserRefreshToken_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserRefreshToken"("shouldUpdateSequenceId", "tenancyId");
134+
135+
-- SPLIT_STATEMENT_SENTINEL
136+
-- SINGLE_STATEMENT_SENTINEL
137+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
138+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserRefreshToken_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserRefreshToken"("tenancyId", "sequenceId");
139+
140+
-- ProjectUserOAuthAccount indexes
141+
-- SPLIT_STATEMENT_SENTINEL
142+
-- SINGLE_STATEMENT_SENTINEL
143+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
144+
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserOAuthAccount_sequenceId_key" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserOAuthAccount"("sequenceId");
145+
146+
-- SPLIT_STATEMENT_SENTINEL
147+
-- SINGLE_STATEMENT_SENTINEL
148+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
149+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserOAuthAccount_shouldUpdateSequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserOAuthAccount"("shouldUpdateSequenceId", "tenancyId");
150+
151+
-- SPLIT_STATEMENT_SENTINEL
152+
-- SINGLE_STATEMENT_SENTINEL
153+
-- RUN_OUTSIDE_TRANSACTION_SENTINEL
154+
CREATE INDEX CONCURRENTLY IF NOT EXISTS "ProjectUserOAuthAccount_tenancyId_sequenceId_idx" ON /* SCHEMA_NAME_SENTINEL */."ProjectUserOAuthAccount"("tenancyId", "sequenceId");

apps/backend/prisma/schema.prisma

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,16 @@ model Team {
179179
serverMetadata Json?
180180
profileImageUrl String?
181181
182+
sequenceId BigInt? @unique
183+
shouldUpdateSequenceId Boolean @default(true)
184+
182185
teamMembers TeamMember[]
183186
projectApiKey ProjectApiKey[]
184187
185188
@@id([tenancyId, teamId])
186189
@@unique([mirroredProjectId, mirroredBranchId, teamId])
190+
@@index([tenancyId, sequenceId], name: "Team_tenancyId_sequenceId_idx")
191+
@@index([shouldUpdateSequenceId, tenancyId], name: "Team_shouldUpdateSequenceId_idx")
187192
}
188193

189194
// This is used for fields that are boolean but only the true value is part of a unique constraint.
@@ -205,6 +210,9 @@ model TeamMember {
205210
createdAt DateTime @default(now())
206211
updatedAt DateTime @updatedAt
207212
213+
sequenceId BigInt? @unique
214+
shouldUpdateSequenceId Boolean @default(true)
215+
208216
projectUser ProjectUser @relation(fields: [tenancyId, projectUserId], references: [tenancyId, projectUserId], onDelete: Cascade)
209217
team Team @relation(fields: [tenancyId, teamId], references: [tenancyId, teamId], onDelete: Cascade)
210218
isSelected BooleanTrue?
@@ -213,6 +221,8 @@ model TeamMember {
213221
@@id([tenancyId, projectUserId, teamId])
214222
@@unique([tenancyId, projectUserId, isSelected])
215223
@@index([tenancyId, projectUserId, isSelected], map: "TeamMember_projectUserId_isSelected_idx")
224+
@@index([tenancyId, sequenceId], name: "TeamMember_tenancyId_sequenceId_idx")
225+
@@index([shouldUpdateSequenceId, tenancyId], name: "TeamMember_shouldUpdateSequenceId_idx")
216226
}
217227

218228
model ProjectUserDirectPermission {
@@ -226,7 +236,12 @@ model ProjectUserDirectPermission {
226236
227237
projectUser ProjectUser @relation(fields: [tenancyId, projectUserId], references: [tenancyId, projectUserId], onDelete: Cascade)
228238
239+
sequenceId BigInt? @unique
240+
shouldUpdateSequenceId Boolean @default(true)
241+
229242
@@unique([tenancyId, projectUserId, permissionId])
243+
@@index([shouldUpdateSequenceId, tenancyId], name: "ProjectUserDirectPermission_shouldUpdateSequenceId_idx")
244+
@@index([tenancyId, sequenceId], name: "ProjectUserDirectPermission_tenancyId_sequenceId_idx")
230245
}
231246

232247
model TeamMemberDirectPermission {
@@ -241,7 +256,12 @@ model TeamMemberDirectPermission {
241256
242257
teamMember TeamMember @relation(fields: [tenancyId, projectUserId, teamId], references: [tenancyId, projectUserId, teamId], onDelete: Cascade)
243258
259+
sequenceId BigInt? @unique
260+
shouldUpdateSequenceId Boolean @default(true)
261+
244262
@@unique([tenancyId, projectUserId, teamId, permissionId])
263+
@@index([shouldUpdateSequenceId, tenancyId], name: "TeamMemberDirectPermission_shouldUpdateSequenceId_idx")
264+
@@index([tenancyId, sequenceId], name: "TeamMemberDirectPermission_tenancyId_sequenceId_idx")
245265
}
246266

247267
model ProjectUser {
@@ -343,9 +363,14 @@ model ProjectUserOAuthAccount {
343363
allowConnectedAccounts Boolean @default(true)
344364
allowSignIn Boolean @default(true)
345365
366+
sequenceId BigInt? @unique
367+
shouldUpdateSequenceId Boolean @default(true)
368+
346369
@@id([tenancyId, id])
347370
@@unique([tenancyId, configOAuthProviderId, projectUserId, providerAccountId])
348371
@@index([tenancyId, projectUserId])
372+
@@index([tenancyId, sequenceId], name: "ProjectUserOAuthAccount_tenancyId_sequenceId_idx")
373+
@@index([shouldUpdateSequenceId, tenancyId], name: "ProjectUserOAuthAccount_shouldUpdateSequenceId_idx")
349374
}
350375

351376
model SessionReplay {
@@ -361,6 +386,9 @@ model SessionReplay {
361386
createdAt DateTime @default(now())
362387
updatedAt DateTime @updatedAt
363388
389+
sequenceId BigInt? @unique
390+
shouldUpdateSequenceId Boolean @default(true)
391+
364392
projectUser ProjectUser @relation(fields: [tenancyId, projectUserId], references: [tenancyId, projectUserId], onDelete: Cascade)
365393
tenancy Tenancy @relation(fields: [tenancyId], references: [id], onDelete: Cascade)
366394
@@ -371,6 +399,8 @@ model SessionReplay {
371399
@@index([tenancyId, lastEventAt])
372400
// index by updatedAt instead of lastEventAt because event timing can be spoofed
373401
@@index([tenancyId, refreshTokenId, updatedAt])
402+
@@index([tenancyId, sequenceId], name: "SessionReplay_tenancyId_sequenceId_idx")
403+
@@index([shouldUpdateSequenceId, tenancyId], name: "SessionReplay_shouldUpdateSequenceId_idx")
374404
@@map("SessionReplay")
375405
}
376406

@@ -612,7 +642,12 @@ model ProjectUserRefreshToken {
612642
expiresAt DateTime?
613643
isImpersonation Boolean @default(false)
614644
645+
sequenceId BigInt? @unique
646+
shouldUpdateSequenceId Boolean @default(true)
647+
615648
@@id([tenancyId, id])
649+
@@index([tenancyId, sequenceId], name: "ProjectUserRefreshToken_tenancyId_sequenceId_idx")
650+
@@index([shouldUpdateSequenceId, tenancyId], name: "ProjectUserRefreshToken_shouldUpdateSequenceId_idx")
616651
}
617652

618653
model ProjectUserAuthorizationCode {
@@ -655,9 +690,13 @@ model VerificationCode {
655690
data Json
656691
attemptCount Int @default(0)
657692
693+
sequenceId BigInt? @unique
694+
shouldUpdateSequenceId Boolean @default(true)
695+
658696
@@id([projectId, branchId, id])
659697
@@unique([projectId, branchId, code])
660698
@@index([data(ops: JsonbPathOps)], type: Gin)
699+
@@index([shouldUpdateSequenceId, type], name: "VerificationCode_shouldUpdateSequenceId_type_idx")
661700
}
662701

663702
enum VerificationCodeType {
@@ -1012,13 +1051,18 @@ model EmailOutbox {
10121051
unsubscribedAt DateTime?
10131052
markedAsSpamAt DateTime?
10141053
1054+
sequenceId BigInt? @unique
1055+
shouldUpdateSequenceId Boolean @default(true)
1056+
10151057
tenancy Tenancy @relation(fields: [tenancyId], references: [id], onDelete: Cascade)
10161058
10171059
@@id([tenancyId, id])
10181060
@@index([tenancyId, finishedSendingAt(sort: Desc), scheduledAtIfNotYetQueued(sort: Desc), priority, id], map: "EmailOutbox_ordering_idx")
10191061
@@index([tenancyId, simpleStatus], map: "EmailOutbox_simple_status_tenancy_idx")
10201062
@@index([tenancyId, status], map: "EmailOutbox_status_tenancy_idx")
10211063
@@index([isQueued], map: "EmailOutbox_isQueued_idx")
1064+
@@index([tenancyId, sequenceId], name: "EmailOutbox_tenancyId_sequenceId_idx")
1065+
@@index([shouldUpdateSequenceId, tenancyId], name: "EmailOutbox_shouldUpdateSequenceId_idx")
10221066
}
10231067

10241068
model EmailOutboxProcessingMetadata {
@@ -1078,8 +1122,13 @@ model UserNotificationPreference {
10781122
enabled Boolean
10791123
projectUser ProjectUser @relation(fields: [tenancyId, projectUserId], references: [tenancyId, projectUserId], onDelete: Cascade)
10801124
1125+
sequenceId BigInt? @unique
1126+
shouldUpdateSequenceId Boolean @default(true)
1127+
10811128
@@id([tenancyId, id])
10821129
@@unique([tenancyId, projectUserId, notificationCategoryId])
1130+
@@index([shouldUpdateSequenceId, tenancyId], name: "UserNotificationPreference_shouldUpdateSequenceId_idx")
1131+
@@index([tenancyId, sequenceId], name: "UserNotificationPreference_tenancyId_sequenceId_idx")
10831132
}
10841133

10851134
model ThreadMessage {

0 commit comments

Comments
 (0)