feat: 实现公平捞瓶和领取租约
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
ALTER TABLE "bottles" ADD COLUMN "active_lease_id" UUID;
|
||||
-- Legacy leases predate recoverable idempotency tokens. Expire them rather than
|
||||
-- fabricating ciphertext that could ever be returned as a valid bearer secret.
|
||||
ALTER TABLE "bottle_pick_leases" ADD COLUMN "lease_token_ciphertext" BYTEA;
|
||||
WITH expired AS (
|
||||
UPDATE "bottle_pick_leases"
|
||||
SET "status" = 'EXPIRED', "ended_at" = COALESCE("ended_at", CURRENT_TIMESTAMP)
|
||||
WHERE "status" = 'ACTIVE'
|
||||
RETURNING "bottle_id"
|
||||
)
|
||||
UPDATE "bottles" b
|
||||
SET "pool_status" = 'IN_POOL', "version" = b."version" + 1
|
||||
WHERE b."pool_status" = 'LEASED'
|
||||
AND b."id" IN (SELECT "bottle_id" FROM expired);
|
||||
UPDATE "bottle_pick_leases"
|
||||
SET "lease_token_ciphertext" = decode(repeat('00', 48), 'hex')
|
||||
WHERE "lease_token_ciphertext" IS NULL;
|
||||
ALTER TABLE "bottle_pick_leases"
|
||||
ALTER COLUMN "lease_token_ciphertext" SET NOT NULL;
|
||||
|
||||
CREATE TABLE "bottle_pick_requests" (
|
||||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||||
"picker_id" UUID NOT NULL,
|
||||
"request_id" VARCHAR(128) NOT NULL,
|
||||
"lease_id" UUID,
|
||||
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT "bottle_pick_requests_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "bottles_active_lease_id_key" ON "bottles"("active_lease_id");
|
||||
CREATE UNIQUE INDEX "bottle_pick_requests_picker_id_request_id_key" ON "bottle_pick_requests"("picker_id", "request_id");
|
||||
CREATE UNIQUE INDEX "bottle_pick_requests_lease_id_key" ON "bottle_pick_requests"("lease_id");
|
||||
CREATE INDEX "bottle_pick_requests_created_at_idx" ON "bottle_pick_requests"("created_at");
|
||||
ALTER TABLE "bottles" ADD CONSTRAINT "bottles_active_lease_id_fkey" FOREIGN KEY ("active_lease_id") REFERENCES "bottle_pick_leases"("id") ON DELETE SET NULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
|
||||
ALTER TABLE "bottle_pick_requests" ADD CONSTRAINT "bottle_pick_requests_picker_id_fkey" FOREIGN KEY ("picker_id") REFERENCES "accounts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "bottle_pick_requests" ADD CONSTRAINT "bottle_pick_requests_lease_id_fkey" FOREIGN KEY ("lease_id") REFERENCES "bottle_pick_leases"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
+30
-10
@@ -118,6 +118,7 @@ model Account {
|
||||
notifications Notification[]
|
||||
auditLogs AuditLog[] @relation("AuditActor")
|
||||
dailyUsage DailyUsage[]
|
||||
pickRequests BottlePickRequest[]
|
||||
|
||||
@@map("accounts")
|
||||
}
|
||||
@@ -213,6 +214,8 @@ model Bottle {
|
||||
pickHistory BottlePickHistory[]
|
||||
conversation Conversation?
|
||||
reports Report[]
|
||||
activeLeaseId String? @unique @map("active_lease_id") @db.Uuid
|
||||
activeLease BottlePickLease? @relation("ActiveBottleLease", fields: [activeLeaseId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@unique([authorId, clientRequestId])
|
||||
@@index([poolStatus, createdAt])
|
||||
@@ -223,22 +226,39 @@ model Bottle {
|
||||
|
||||
model BottlePickLease {
|
||||
/// Partial unique index bottle_pick_leases_one_active_per_bottle is managed in 0001_init SQL.
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
bottleId String @map("bottle_id") @db.Uuid
|
||||
pickerId String @map("picker_id") @db.Uuid
|
||||
leaseTokenHash String @unique @map("lease_token_hash") @db.VarChar(255)
|
||||
expiresAt DateTime @map("expires_at") @db.Timestamptz(3)
|
||||
status BottlePickLeaseStatus @default(ACTIVE)
|
||||
endedAt DateTime? @map("ended_at") @db.Timestamptz(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
bottle Bottle @relation(fields: [bottleId], references: [id], onDelete: Cascade)
|
||||
picker Account @relation("PickerLeases", fields: [pickerId], references: [id], onDelete: Cascade)
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
bottleId String @map("bottle_id") @db.Uuid
|
||||
pickerId String @map("picker_id") @db.Uuid
|
||||
leaseTokenHash String @unique @map("lease_token_hash") @db.VarChar(255)
|
||||
leaseTokenCiphertext Bytes @map("lease_token_ciphertext")
|
||||
expiresAt DateTime @map("expires_at") @db.Timestamptz(3)
|
||||
status BottlePickLeaseStatus @default(ACTIVE)
|
||||
endedAt DateTime? @map("ended_at") @db.Timestamptz(3)
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
bottle Bottle @relation(fields: [bottleId], references: [id], onDelete: Cascade)
|
||||
activeForBottle Bottle? @relation("ActiveBottleLease")
|
||||
picker Account @relation("PickerLeases", fields: [pickerId], references: [id], onDelete: Cascade)
|
||||
pickRequest BottlePickRequest?
|
||||
|
||||
@@index([pickerId, expiresAt])
|
||||
@@index([bottleId, expiresAt])
|
||||
@@map("bottle_pick_leases")
|
||||
}
|
||||
|
||||
model BottlePickRequest {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
pickerId String @map("picker_id") @db.Uuid
|
||||
requestId String @map("request_id") @db.VarChar(128)
|
||||
leaseId String? @unique @map("lease_id") @db.Uuid
|
||||
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
|
||||
picker Account @relation(fields: [pickerId], references: [id], onDelete: Cascade)
|
||||
lease BottlePickLease? @relation(fields: [leaseId], references: [id], onDelete: SetNull)
|
||||
|
||||
@@unique([pickerId, requestId])
|
||||
@@index([createdAt])
|
||||
@@map("bottle_pick_requests")
|
||||
}
|
||||
|
||||
model BottlePickHistory {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
bottleId String @map("bottle_id") @db.Uuid
|
||||
|
||||
Reference in New Issue
Block a user