Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion apps/mobile/e2e/record.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,26 @@
set -euo pipefail

resolve_adb() {
# Mirror apps/mobile/e2e/appium.sh and dev/local/mobile-android.ts so the
# agent PATH never matters (Homebrew android-commandlinetools is the usual
# install; plain `adb` is often absent from the shell).
local candidate found
if [ -z "${ANDROID_HOME:-}" ] && [ -z "${ANDROID_SDK_ROOT:-}" ]; then
for candidate in /opt/homebrew/share/android-commandlinetools /usr/local/share/android-commandlinetools "$HOME/Library/Android/sdk"; do
if [ -x "$candidate/platform-tools/adb" ]; then
export ANDROID_HOME="$candidate" ANDROID_SDK_ROOT="$candidate"
break
fi
done
fi
found=$(command -v adb 2>/dev/null || true)
for candidate in "$found" "${ANDROID_HOME:-}/platform-tools/adb" "$HOME/Library/Android/sdk/platform-tools/adb"; do
for candidate in \
"$found" \
"${ANDROID_HOME:-}/platform-tools/adb" \
"${ANDROID_SDK_ROOT:-}/platform-tools/adb" \
/opt/homebrew/share/android-commandlinetools/platform-tools/adb \
/usr/local/share/android-commandlinetools/platform-tools/adb \
"$HOME/Library/Android/sdk/platform-tools/adb"; do
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
printf '%s' "$candidate"
return 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { NextResponse } from 'next/server';
import { captureException } from '@sentry/nextjs';
import { db } from '@/lib/drizzle';
import { CRON_SECRET } from '@/lib/config.server';
import { sql } from 'drizzle-orm';
import { format } from 'date-fns';
import { provisionModelExperimentRequestPartitions } from '@/lib/model-experiment-request-partitions';

if (!CRON_SECRET) {
throw new Error('CRON_SECRET is not configured in environment variables');
Expand All @@ -19,31 +18,15 @@ export async function GET(request: Request) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const now = new Date();
const created: string[] = [];
const errors: string[] = [];

for (let offset = 0; offset <= 2; offset++) {
const target = new Date(now.getFullYear(), now.getMonth() + offset, 1);
const nextMonth = new Date(target.getFullYear(), target.getMonth() + 1, 1);
const name = `model_experiment_request_${format(target, 'yyyy_MM')}`;

try {
await db.execute(
sql.raw(
`CREATE TABLE IF NOT EXISTS "${name}" PARTITION OF "model_experiment_request" FOR VALUES FROM ('${format(target, 'yyyy-MM-dd')}') TO ('${format(nextMonth, 'yyyy-MM-dd')}')`
)
);
created.push(name);
} catch (error) {
const message = `Failed to create partition ${name}: ${error instanceof Error ? error.message : String(error)}`;
console.error(`[model-experiment-request-partition-maintenance] ${message}`);
captureException(error, {
tags: { source: 'model-experiment-request-partition-maintenance', partition: name },
});
errors.push(message);
}
}
const { created, errors: partitionErrors } = await provisionModelExperimentRequestPartitions(db);
const errors = partitionErrors.map(({ name, error }) => {
const message = `Failed to create partition ${name}: ${error instanceof Error ? error.message : String(error)}`;
console.error(`[model-experiment-request-partition-maintenance] ${message}`);
captureException(error, {
tags: { source: 'model-experiment-request-partition-maintenance', partition: name },
});
return message;
});

console.log(
`[model-experiment-request-partition-maintenance] created=[${created.join(', ')}] errors=${errors.length}`
Expand Down
57 changes: 57 additions & 0 deletions apps/web/src/lib/model-experiment-request-partitions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { describe, expect, test } from '@jest/globals';
import { PgDialect } from 'drizzle-orm/pg-core';
import type { SQL } from 'drizzle-orm';
import { provisionModelExperimentRequestPartitions } from '@/lib/model-experiment-request-partitions';

describe('model experiment request partitions', () => {
test('provisions current month and two months ahead', async () => {
const statements: string[] = [];
const dialect = new PgDialect();
const fakeDb = {
execute: async (query: SQL) => {
statements.push(dialect.sqlToQuery(query).sql);
return { rows: [] };
},
};

const result = await provisionModelExperimentRequestPartitions(
fakeDb as never,
new Date(2026, 7, 15, 12) // August 2026
);

expect(result).toEqual({
created: [
'model_experiment_request_2026_08',
'model_experiment_request_2026_09',
'model_experiment_request_2026_10',
],
errors: [],
});
expect(statements).toEqual([
`CREATE TABLE IF NOT EXISTS "model_experiment_request_2026_08" PARTITION OF "model_experiment_request" FOR VALUES FROM ('2026-08-01') TO ('2026-09-01')`,
`CREATE TABLE IF NOT EXISTS "model_experiment_request_2026_09" PARTITION OF "model_experiment_request" FOR VALUES FROM ('2026-09-01') TO ('2026-10-01')`,
`CREATE TABLE IF NOT EXISTS "model_experiment_request_2026_10" PARTITION OF "model_experiment_request" FOR VALUES FROM ('2026-10-01') TO ('2026-11-01')`,
]);
});

test('collects per-partition failures without stopping the window', async () => {
let calls = 0;
const result = await provisionModelExperimentRequestPartitions(
{
execute: async () => {
calls += 1;
if (calls === 2) throw new Error('boom');
return { rows: [] };
},
} as never,
new Date(2026, 7, 1)
);

expect(result.created).toEqual([
'model_experiment_request_2026_08',
'model_experiment_request_2026_10',
]);
expect(result.errors).toHaveLength(1);
expect(result.errors[0]?.name).toBe('model_experiment_request_2026_09');
});
});
44 changes: 44 additions & 0 deletions apps/web/src/lib/model-experiment-request-partitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { db as defaultDb } from '@/lib/drizzle';
import { sql } from 'drizzle-orm';
import { format } from 'date-fns';

type ModelExperimentRequestPartitionDb = Pick<typeof defaultDb, 'execute'>;

export type ModelExperimentRequestPartitionProvisioningResult = {
created: string[];
errors: Array<{ name: string; error: unknown }>;
};

/**
* Creates the current month and next two monthly request-audit partitions.
*
* Production keeps this window current via cron. Fresh migrate snapshots (CI,
* Jest workers) only have the seed months from the partitioning migration, so
* tests must call this before inserting rows whose created_at defaults to now().
*/
export async function provisionModelExperimentRequestPartitions(
fromDb: ModelExperimentRequestPartitionDb,
now: Date = new Date()
): Promise<ModelExperimentRequestPartitionProvisioningResult> {
const created: string[] = [];
const errors: Array<{ name: string; error: unknown }> = [];

for (let offset = 0; offset <= 2; offset++) {
const target = new Date(now.getFullYear(), now.getMonth() + offset, 1);
const nextMonth = new Date(target.getFullYear(), target.getMonth() + 1, 1);
const name = `model_experiment_request_${format(target, 'yyyy_MM')}`;

try {
await fromDb.execute(
sql.raw(
`CREATE TABLE IF NOT EXISTS "${name}" PARTITION OF "model_experiment_request" FOR VALUES FROM ('${format(target, 'yyyy-MM-dd')}') TO ('${format(nextMonth, 'yyyy-MM-dd')}')`
)
);
created.push(name);
} catch (error) {
errors.push({ name, error });
}
}

return { created, errors };
}
10 changes: 10 additions & 0 deletions apps/web/src/tests/setup/workerSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { kiloclaw_subscriptions } from '@kilocode/db/schema';
import { drizzle } from 'drizzle-orm/node-postgres';
import { migrate } from 'drizzle-orm/node-postgres/migrator';
import { provisionExaUsageLogPartitions } from '@/lib/exa-usage-partitions';
import { provisionModelExperimentRequestPartitions } from '@/lib/model-experiment-request-partitions';
import { existsSync, writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import { shutdownPosthog } from '@/lib/posthog';
Expand Down Expand Up @@ -63,6 +64,15 @@ beforeAll(async () => {
`Failed to create Exa usage log partition ${name}: ${error instanceof Error ? error.message : String(error)}`
);
}

const { errors: modelExperimentPartitionErrors } =
await provisionModelExperimentRequestPartitions(testDb);
if (modelExperimentPartitionErrors.length > 0) {
const [{ name, error }] = modelExperimentPartitionErrors;
throw new Error(
`Failed to create model experiment request partition ${name}: ${error instanceof Error ? error.message : String(error)}`
);
}
} finally {
await testPool.end();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS "model_experiment_request_2026_08" PARTITION OF "model_experiment_request"
FOR VALUES FROM ('2026-08-01') TO ('2026-09-01');
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "model_experiment_request_2026_09" PARTITION OF "model_experiment_request"
FOR VALUES FROM ('2026-09-01') TO ('2026-10-01');
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "model_experiment_request_2026_10" PARTITION OF "model_experiment_request"
FOR VALUES FROM ('2026-10-01') TO ('2026-11-01');
Loading