접속 집계 교착으로 인한 턴 정지를 방지
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { createHash, randomUUID } from 'node:crypto';
|
||||
|
||||
import { asRecord, resolveAccessLimitLevel, resolveAccessRefreshLimit } from '@sammo-ts/common';
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import { acquireGameSchemaAdvisoryXactLock, GENERAL_ACCESS_PERSISTENCE_LOCK, GamePrisma } from '@sammo-ts/infra';
|
||||
import type { GameSessionTokenPayload } from '@sammo-ts/common/auth/gameToken';
|
||||
|
||||
import type { DatabaseClient } from '../context.js';
|
||||
@@ -163,7 +163,7 @@ const markBatchProcessed = async (db: Pick<DatabaseClient, '$executeRaw'>, batch
|
||||
};
|
||||
|
||||
export const flushDeferredGeneralAccessBatch = async (
|
||||
db: Pick<DatabaseClient, '$queryRaw' | '$executeRaw' | 'worldState'>,
|
||||
db: Pick<DatabaseClient, '$transaction' | '$queryRaw' | '$executeRaw' | 'worldState'>,
|
||||
batchId: string,
|
||||
entries: readonly DeferredGeneralAccessEntry[]
|
||||
): Promise<{ states: DeferredGeneralAccessFlushRow[]; refreshLimit: number }> => {
|
||||
@@ -171,6 +171,9 @@ export const flushDeferredGeneralAccessBatch = async (
|
||||
await markBatchProcessed(db, batchId);
|
||||
return { states: [], refreshLimit: 0 };
|
||||
}
|
||||
if (!db.$transaction) {
|
||||
throw new Error('Deferred general access persistence requires transaction support.');
|
||||
}
|
||||
const worldState = await db.worldState.findFirst({
|
||||
orderBy: { id: 'asc' },
|
||||
select: {
|
||||
@@ -210,7 +213,11 @@ export const flushDeferredGeneralAccessBatch = async (
|
||||
}))
|
||||
);
|
||||
const periodKey = worldState.currentYear * 12 + worldState.currentMonth - 1;
|
||||
const states = await db.$queryRaw<DeferredGeneralAccessFlushRow[]>(GamePrisma.sql`
|
||||
const states = await db.$transaction(async (transaction) => {
|
||||
// The turn flush and synchronous access path must enter through the same
|
||||
// schema-scoped lock before either traffic or access rows are mutated.
|
||||
await acquireGameSchemaAdvisoryXactLock(transaction, GENERAL_ACCESS_PERSISTENCE_LOCK);
|
||||
return transaction.$queryRaw<DeferredGeneralAccessFlushRow[]>(GamePrisma.sql`
|
||||
WITH inserted_batch AS (
|
||||
INSERT INTO "general_access_batch" ("id")
|
||||
VALUES (${batchId})
|
||||
@@ -353,6 +360,7 @@ export const flushDeferredGeneralAccessBatch = async (
|
||||
END,
|
||||
resolved."weight"
|
||||
FROM resolved
|
||||
ORDER BY resolved."general_id"
|
||||
ON CONFLICT ("general_id") DO UPDATE SET
|
||||
"user_id" = EXCLUDED."user_id",
|
||||
"last_refresh" = GREATEST("general_access_log"."last_refresh", EXCLUDED."last_refresh"),
|
||||
@@ -394,7 +402,8 @@ export const flushDeferredGeneralAccessBatch = async (
|
||||
LEFT JOIN access_updates ON access_updates."general_id" = actor."id"
|
||||
LEFT JOIN "general_access_log" AS access_log ON access_log."general_id" = actor."id"
|
||||
ORDER BY actor."id"
|
||||
`);
|
||||
`);
|
||||
});
|
||||
return {
|
||||
states,
|
||||
refreshLimit: resolveAccessRefreshLimit(worldState.tickSeconds, meta.refreshLimit),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { asRecord, resolveAccessLimitLevel, resolveAccessRefreshLimit, type AccessLimitLevel } from '@sammo-ts/common';
|
||||
import { GamePrisma } from '@sammo-ts/infra';
|
||||
import { acquireGameSchemaAdvisoryXactLock, GENERAL_ACCESS_PERSISTENCE_LOCK, GamePrisma } from '@sammo-ts/infra';
|
||||
|
||||
import type { GameApiContext } from '../context.js';
|
||||
|
||||
@@ -228,6 +228,7 @@ export const upsertGeneralAccess = async (
|
||||
throw new Error('Traffic access persistence requires transaction support.');
|
||||
}
|
||||
await db.$transaction(async (transaction) => {
|
||||
await acquireGameSchemaAdvisoryXactLock(transaction, GENERAL_ACCESS_PERSISTENCE_LOCK);
|
||||
const periodKey = input.year * 12 + input.month - 1;
|
||||
const periodRows = await transaction.$queryRaw<Array<{ id: number }>>(
|
||||
GamePrisma.sql`
|
||||
|
||||
Reference in New Issue
Block a user