fix(stats): make unique users migration idempotent

This commit is contained in:
Adam 2026-06-21 04:38:47 -05:00
parent 233d065dd5
commit 6f0e934573
No known key found for this signature in database
GPG Key ID: 9CB48779AF150E75
5 changed files with 31 additions and 4 deletions

View File

@ -47,7 +47,7 @@ jobs:
VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }}
VITE_SENTRY_RELEASE: web@${{ github.sha }}
- run: bun sst shell --stage=${{ github.ref_name }} -- bun run --cwd packages/stats/core db:migrate
- run: bun sst shell --stage=${{ github.ref_name }} -- bun run --cwd packages/stats/core db:ensure-unique-users
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}

View File

@ -17,6 +17,7 @@
},
"scripts": {
"db:generate": "drizzle-kit generate --config=drizzle.config.ts",
"db:ensure-unique-users": "bun src/ensure-unique-users.ts",
"db:migrate": "bun src/migrate.ts",
"db:push": "drizzle-kit push --config=drizzle.config.ts",
"db:studio": "drizzle-kit studio --config=drizzle.config.ts",

View File

@ -17,7 +17,7 @@ export type AthenaData = Record<string, string>
export class AthenaQueryError extends Schema.TaggedErrorClass<AthenaQueryError>()("AthenaQueryError", {
message: Schema.String,
queryExecutionId: Schema.optional(Schema.String),
cause: Schema.optional(Schema.Defect()),
cause: Schema.optional(Schema.Defect),
}) {}
export class AthenaQueryTimeoutError extends Schema.TaggedErrorClass<AthenaQueryTimeoutError>()(

View File

@ -45,14 +45,14 @@ export class DrizzleClient extends Context.Service<DrizzleClient, Drizzle>()("@o
}
export class DatabaseError extends Schema.TaggedErrorClass<DatabaseError>()("DatabaseError", {
cause: Schema.Defect(),
cause: Schema.Defect,
}) {}
export const catchDbError = Effect.mapError((cause) => DatabaseError.make({ cause }))
export class MigrationError extends Schema.TaggedErrorClass<MigrationError>()("MigrationError", {
message: Schema.String,
cause: Schema.optional(Schema.Defect()),
cause: Schema.optional(Schema.Defect),
}) {}
export const migrate = Effect.fn("Database.migrate")(function* () {

View File

@ -0,0 +1,26 @@
import { Client } from "@planetscale/database"
import { Resource } from "sst/resource"
const tables = ["geo_stat", "model_stat", "provider_stat"] as const
const client = new Client({ url: Resource.StatsDatabase.url })
await tables.reduce(
(promise, table) => promise.then(() => ensureUniqueUsersColumn(table)),
Promise.resolve(),
)
async function ensureUniqueUsersColumn(table: (typeof tables)[number]) {
const result = await client.execute<{ column_name: string }>(
"SELECT column_name FROM information_schema.columns WHERE table_schema = database() AND table_name = ? AND column_name = 'unique_users'",
[table],
)
if (result.rows.length > 0) {
console.log(`unique_users column already exists on ${table}`)
return
}
await client.execute(`ALTER TABLE \`${table}\` ADD \`unique_users\` bigint NOT NULL DEFAULT 0`)
console.log(`added unique_users column to ${table}`)
}