Browse Source

fix(stats): make unique users migration idempotent

Adam 2 months ago
parent
commit
6f0e934573

+ 1 - 1
.github/workflows/deploy.yml

@@ -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 }}

+ 1 - 0
packages/stats/core/package.json

@@ -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",

+ 1 - 1
packages/stats/core/src/athena.ts

@@ -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>()(

+ 2 - 2
packages/stats/core/src/database.ts

@@ -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* () {

+ 26 - 0
packages/stats/core/src/ensure-unique-users.ts

@@ -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}`)
+}