chore(release): prepare v1.31.4

This commit is contained in:
Marc-André Bombeck committed 2026-07-21 01:34:29 +02:00
1 parent 3ce6726646
commit b38f41b99d
12 files changed
+99 -68

No files matched your search

+16 -16
View File
@@ -274,6 +274,22 @@ export const POST = apiHandler(async (request: NextRequest) => {
select: { id: true },
});
// Keep channel reconciliation under the same identity locks as the device
// row. Moving it after commit lets two first-time registrations race the
// compound unique key and intermittently turns one valid request into 500.
if (apnsToken) {
await tx.notificationChannel.upsert({
where: { userId_type: { userId: user.id, type: "APNS" } },
create: {
userId: user.id,
type: "APNS",
enabled: true,
config: encrypt("{}"),
},
update: {},
});
}
return { id: device.id };
});
@@ -292,22 +308,6 @@ export const POST = apiHandler(async (request: NextRequest) => {
const { id } = registration;
// Reconcile APNs channel state only after the canonical device transaction
// commits. Config is an encrypted empty record by design — the per-device
// token and environment live on the Device row.
if (apnsToken) {
await prisma.notificationChannel.upsert({
where: { userId_type: { userId: user.id, type: "APNS" } },
create: {
userId: user.id,
type: "APNS",
enabled: true,
config: encrypt("{}"),
},
update: {},
});
}
await auditLog("devices.register", {
userId: user.id,
details: {
+1 -3
View File
@@ -42,9 +42,7 @@ describe("per-process database connection budget", () => {
expect(getConnectionBudget()).toBe(20);
expect(getPrismaPoolMax()).toBe(18);
expect(getPgBossPoolMax()).toBe(2);
expect(getPrismaPoolMax() + getPgBossPoolMax()).toBe(
getConnectionBudget(),
);
expect(getPrismaPoolMax() + getPgBossPoolMax()).toBe(getConnectionBudget());
});
it("uses the existing pool_timeout seconds for both pool constructors", () => {
+1 -3
View File
@@ -3,9 +3,7 @@ import { beforeEach, describe, expect, it, vi, type Mock } from "vitest";
interface MockBoss {
start: Mock<() => Promise<unknown>>;
stop: Mock<(options?: { graceful?: boolean }) => Promise<void>>;
on: Mock<
(event: string, handler: (error: Error) => void) => MockBoss
>;
on: Mock<(event: string, handler: (error: Error) => void) => MockBoss>;
emitError(error: Error): void;
}
+9 -8
View File
@@ -7,10 +7,7 @@
*/
import { PgBoss } from "pg-boss";
import {
getPgBossPoolMax,
getPoolConnectionTimeoutMs,
} from "@/lib/db";
import { getPgBossPoolMax, getPoolConnectionTimeoutMs } from "@/lib/db";
const BOSS_KEY = "__healthlog_pgboss__" as const;
const BOSS_START_KEY = "__healthlog_pgboss_start__" as const;
@@ -36,7 +33,9 @@ function wait(ms: number): Promise<void> {
return promise;
}
function resolveRetryPolicy(options: ProducerStartOptions): ProducerRetryPolicy {
function resolveRetryPolicy(
options: ProducerStartOptions,
): ProducerRetryPolicy {
const maxAttempts = Math.max(
1,
Math.trunc(options.maxAttempts ?? PRODUCER_START_MAX_ATTEMPTS),
@@ -52,7 +51,10 @@ function resolveRetryPolicy(options: ProducerStartOptions): ProducerRetryPolicy
return { maxAttempts, baseDelayMs, maxDelayMs };
}
function retryDelayMs(failedAttempt: number, policy: ProducerRetryPolicy): number {
function retryDelayMs(
failedAttempt: number,
policy: ProducerRetryPolicy,
): number {
return Math.min(
policy.maxDelayMs,
policy.baseDelayMs * 2 ** Math.max(0, failedAttempt - 1),
@@ -164,8 +166,7 @@ export async function startGlobalBossProducer(
if (existing) return existing;
const starting = globalState[BOSS_START_KEY] as
| Promise<PgBoss | null>
| undefined;
Promise<PgBoss | null> | undefined;
if (starting) return starting;
const startPromise = startProducerAttempts(