Skip to content

Commit 8db39d7

Browse files
committed
improvement(search): run Search retirement as a throttled background migration
The deploy advances the retirement for one minute and defers; a scheduled workflow advances it between deploys in resumable slices paced on commit latency, WAL rate and replication lag, starts index maintenance only in an off-peak window, and journals 0029 once everything is finished.
1 parent 76a82b0 commit 8db39d7

6 files changed

Lines changed: 1084 additions & 335 deletions
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
name: Search Retirement Cleanup
2+
3+
# Background runner for the `0029_retire_all_search_embeddings` script migration. A deploy only
4+
# advances the cleanup for a minute and defers; this job advances it between deploys in budgeted,
5+
# resumable slices throttled on the database's commit latency, WAL rate and replication lag, and
6+
# journals it once retirement and index maintenance finish. Every slice resumes the saved cursor
7+
# and maintenance checkpoints, so a cancelled or failed run loses at most its in-flight page.
8+
# Runbook: packages/db/script-migrations/search-embedding-retirement.md
9+
#
10+
# Pause: `gh workflow disable search-retirement.yml`, then cancel any in-progress run.
11+
12+
on:
13+
schedule:
14+
# Retirement slice every hour; a slice never starts index maintenance.
15+
- cron: '23 * * * *'
16+
# Off-peak maintenance window: concurrent HNSW rebuilds and vacuums may start.
17+
- cron: '7 3 * * *'
18+
workflow_dispatch:
19+
inputs:
20+
environment:
21+
description: Target environment
22+
required: true
23+
type: choice
24+
options:
25+
- production
26+
- staging
27+
budget_minutes:
28+
description: Minutes after which no page or maintenance operation starts
29+
required: false
30+
default: '50'
31+
maintenance:
32+
description: Allow concurrent index rebuilds and vacuums in this run
33+
type: boolean
34+
required: false
35+
default: false
36+
37+
permissions:
38+
contents: read
39+
40+
jobs:
41+
cleanup:
42+
name: Advance Search retirement (${{ matrix.environment }})
43+
if: github.repository == 'simstudioai/sim'
44+
runs-on: ${{ (vars.CI_PROVIDER == '' || vars.CI_PROVIDER == 'blacksmith') && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
45+
strategy:
46+
fail-fast: false
47+
matrix:
48+
environment: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', inputs.environment) || '["production","staging"]') }}
49+
# One slice per database at a time; a queued slice waits rather than cancelling a running one.
50+
concurrency:
51+
group: search-retirement-${{ matrix.environment }}
52+
cancel-in-progress: false
53+
# A rebuild that starts inside the budget runs to completion, so the job outlives the budget.
54+
timeout-minutes: 300
55+
56+
steps:
57+
- name: Checkout code
58+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
59+
60+
- name: Setup Bun
61+
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
62+
with:
63+
bun-version: 1.4.2
64+
65+
- name: Install dependencies
66+
run: bun install --frozen-lockfile --ignore-scripts
67+
68+
# Same secret mapping as migrations.yml: the migration role owns the tables, which the
69+
# concurrent rebuilds and vacuums require, and can read replication lag.
70+
- name: Run a cleanup slice
71+
working-directory: ./packages/db
72+
env:
73+
DATABASE_URL: ${{ matrix.environment == 'production' && secrets.DATABASE_URL || matrix.environment == 'staging' && secrets.STAGING_DATABASE_URL || '' }}
74+
MIGRATION_DATABASE_URL: ${{ matrix.environment == 'production' && secrets.MIGRATION_DATABASE_URL || matrix.environment == 'staging' && secrets.STAGING_MIGRATION_DATABASE_URL || '' }}
75+
BUDGET_MINUTES: ${{ github.event_name == 'workflow_dispatch' && inputs.budget_minutes || github.event.schedule == '7 3 * * *' && '120' || '50' }}
76+
MAINTENANCE: ${{ (github.event_name == 'workflow_dispatch' && inputs.maintenance) || github.event.schedule == '7 3 * * *' }}
77+
run: |
78+
set -euo pipefail
79+
if [ -z "$DATABASE_URL" ]; then
80+
echo "ERROR: no database URL secret resolved" >&2
81+
exit 1
82+
fi
83+
args=(--budget-minutes "$BUDGET_MINUTES")
84+
if [ "$MAINTENANCE" = "true" ]; then
85+
args+=(--maintenance)
86+
fi
87+
bun run ./script-migrations/0029_retire_all_search_embeddings.ts "${args[@]}"

0 commit comments

Comments
 (0)