Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
25d0868
Add design spec for single-table FTS query shape
medihack Aug 29, 2026
b31ea9c
Correct vacuum guidance in the FTS projection spec
medihack Aug 30, 2026
9ec299f
Name the Postgres tuning env vars in the FTS projection spec
medihack Aug 30, 2026
fd19c3f
Follow the project's env var convention for Postgres tuning knobs
medihack Aug 30, 2026
92470c2
Fix date filter sargability and rework the projection indexes
medihack Aug 30, 2026
ed2fe00
Final review pass over the FTS projection spec
medihack Aug 30, 2026
f6d477a
Measure the group_ids GIN index instead of assuming it
medihack Aug 30, 2026
5890c83
Re-measure at the 8M design target and correct three claims
medihack Aug 30, 2026
0986e1c
Label the remaining figures with the rig they came from
medihack Aug 30, 2026
9520593
Add implementation plan for the FTS search projection
medihack Aug 30, 2026
a1d3fbb
Add search projection columns to ReportSearchIndex
medihack Aug 30, 2026
0d113ff
Maintain the search projection with statement-level triggers
medihack Aug 30, 2026
384ef45
Add positive coverage for the modality projection triggers
medihack Aug 30, 2026
f3fe651
Correct Task 3's signals.py snippet in the implementation plan
medihack Aug 30, 2026
787500f
Populate the search projection when index rows are created
medihack Aug 30, 2026
0b90ec9
Fix vacuous creation test to actually exercise sync_projection
medihack Aug 30, 2026
15c71d7
Backfill the search projection in 50k-row chunks
medihack Aug 30, 2026
1c1f985
Add test that exercises the migration's backfill() SQL directly
medihack Aug 30, 2026
a0469f4
Add the search projection indexes and disable GIN fastupdate
medihack Aug 30, 2026
c4576a9
Add check_search_projection drift detection command
medihack Aug 30, 2026
bbbc04c
Make the FTS filter query single-table
medihack Aug 30, 2026
349418e
Strengthen the filter-query equivalence suite per review
medihack Aug 30, 2026
8418652
Read language_code from the projection instead of joining
medihack Aug 30, 2026
0cea4ad
Assert the candidate query plan stays single-table
medihack Aug 30, 2026
622b5d9
Correct the plan-shape assertion in the spec and plan
medihack Aug 30, 2026
3ef44a4
Fix plan-shape test's dedup check to be scale-independent
medihack Aug 30, 2026
ee139c7
Tune PostgreSQL parallelism for the search scan
medihack Aug 30, 2026
8e8e306
Give the init wait a timeout the backfill cannot outrun
medihack Aug 30, 2026
675aeeb
Say which direction an empty group_ids fails in
medihack Aug 30, 2026
b4f08a3
Exercise the backfill chunk loop and multi-row group deletes
medihack Aug 30, 2026
a20543a
Lock projection rows before rewriting them
medihack Aug 30, 2026
ad89bdd
Analyze the search index after creating its indexes
medihack Aug 30, 2026
cf5a0e9
Assert the plan shape of the query the provider actually runs
medihack Aug 30, 2026
4fe9301
Cross-reference the four projection copies and fix two comments
medihack Aug 30, 2026
11d8557
Report how many reports have no search index row yet
medihack Aug 30, 2026
6ef6228
Trim the migration-wait comments to the durable constraint
medihack Aug 31, 2026
7a55d43
Streamline projection comments
medihack Aug 31, 2026
4788985
Correct the spec where the implementation diverged
medihack Aug 31, 2026
ad0877f
Correct the bloat penalty with measured numbers
medihack Aug 31, 2026
6833e5f
Document reclaiming space after the projection migration
medihack Aug 31, 2026
f9d58b6
Correct the claim that the backfill bloat resolves on its own
medihack Aug 31, 2026
0eec396
Document VACUUM FULL as the compaction procedure
medihack Aug 31, 2026
6a44952
Merge branch 'main' into fts-query-shape-performance
medihack Sep 3, 2026
0490299
Drop and rebuild the HNSW index around the projection backfill
samuelvkwong Sep 23, 2026
4f5b6c6
Make the search index admin read-only
samuelvkwong Sep 23, 2026
24a1b60
Sync the projection through language and modality code renames
samuelvkwong Sep 23, 2026
ef11d3e
Tune the HNSW rebuild through server GUCs instead of app settings
samuelvkwong Sep 23, 2026
c82d486
Leave postgres maintenance tuning to the deployment
samuelvkwong Sep 23, 2026
e2a7b38
Revert "Leave postgres maintenance tuning to the deployment"
samuelvkwong Sep 23, 2026
2f0df7b
Squash the projection migrations to their transaction boundaries
samuelvkwong Sep 23, 2026
6be5645
Name the real single-table guard test in the filter-query comment
samuelvkwong Sep 23, 2026
45f8435
Merge branch 'main' into fts-query-shape-performance
samuelvkwong Sep 23, 2026
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
24 changes: 24 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,30 @@ Worker-crash recovery (`radis.core`):

Labeling uses the shared core LLM client (`radis.core.utils.llm_client`); its timeout, rate-limit gate, and transient-retry knobs are the global `LLM_REQUEST_TIMEOUT_SECONDS`, `LLM_RATE_LIMIT_*`, and `LLM_TRANSIENT_RETRY_*` settings.

PostgreSQL parallelism (compose-only overrides, not in `example.env` — set in `.env`
only to override the compose file's default):

- `POSTGRES_MAX_PARALLEL_WORKERS_PER_GATHER`: Parallel workers per query node scanning
the report index (default `4`, raised from PostgreSQL's own default of `2`)
- `POSTGRES_MAX_PARALLEL_WORKERS`, `POSTGRES_MAX_WORKER_PROCESSES`: Cluster-wide worker
pool sizes (each defaults to `8`, PostgreSQL's own default). Raise together with
`POSTGRES_MAX_PARALLEL_WORKERS_PER_GATHER` on a larger host
- `POSTGRES_SHARED_BUFFERS`: PostgreSQL's shared memory buffer (default `128MB`,
PostgreSQL's own default)
- `POSTGRES_MAINTENANCE_WORK_MEM`: memory for VACUUM and index builds (default
`64MB`, PostgreSQL's own default). Raise for the migration window on a large
embedded archive so the pgsearch 0005 HNSW rebuild assembles its graph in
memory (a 1.7M-vector corpus built in 7m36s with `16GB` and 7 workers), and
for the manual rebuilds in the admin guide's compaction section
- `POSTGRES_MAX_PARALLEL_MAINTENANCE_WORKERS`: parallel workers per index build
(default `2`, PostgreSQL's own default); also capped by
`POSTGRES_MAX_PARALLEL_WORKERS`
- `POSTGRES_SHM_SIZE_BYTES`: tmpfs size of the container's `/dev/shm` in bytes
(default `1073741824` = 1 GiB — a cap, not a reservation; Docker's own 64 MB
default is too small for parallel queries). Parallel maintenance
pre-allocates its whole `maintenance_work_mem` budget here, so keep it at
least as large as `POSTGRES_MAINTENANCE_WORK_MEM`

## Code Standards

- **Style Guide**: Google Python Style Guide
Expand Down
25 changes: 25 additions & 0 deletions docker-compose.base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,33 @@ services:
postgres:
image: pgvector/pgvector:pg17@sha256:cf134a767f474095eeba57e0117be8e568e011a63f33fbf252f14c9b760f8e6f
hostname: postgres.local
command:
- postgres
- -c
- max_parallel_workers_per_gather=${POSTGRES_MAX_PARALLEL_WORKERS_PER_GATHER:-4}
- -c
- max_parallel_workers=${POSTGRES_MAX_PARALLEL_WORKERS:-8}
- -c
- max_worker_processes=${POSTGRES_MAX_WORKER_PROCESSES:-8}
- -c
- shared_buffers=${POSTGRES_SHARED_BUFFERS:-128MB}
- -c
- maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-64MB}
- -c
- max_parallel_maintenance_workers=${POSTGRES_MAX_PARALLEL_MAINTENANCE_WORKERS:-2}
volumes:
- postgres_data:/var/lib/postgresql/data
# PostgreSQL's parallel workers exchange data through dynamic shared
# memory in /dev/shm, and Docker's default segment is only 64 MB.
# Parallel maintenance (index builds, e.g. the HNSW rebuild in pgsearch
# migration 0005) pre-allocates its whole maintenance_work_mem budget
# there, so size this at least as large as POSTGRES_MAINTENANCE_WORK_MEM.
# A tmpfs size is a cap, not a reservation. Bytes, because
# `docker stack deploy` rejects unit suffixes here (default: 1 GiB).
- type: tmpfs
target: /dev/shm
tmpfs:
size: ${POSTGRES_SHM_SIZE_BYTES:-1073741824}

volumes:
postgres_data:
7 changes: 5 additions & 2 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ services:
profiles:
- never

# In development `web` runs the migrations the workers below wait for, so
# their WAIT_INIT_TIMEOUT must outlast the longest migration too. Dev has no
# restart policy, so a timeout leaves the worker containers exited.
Comment on lines +29 to +31
web:
<<: *default-app
image: radis_dev-web:latest
Expand All @@ -52,7 +55,7 @@ services:
command: >
bash -c "
wait-for-it -s postgres.local:5432 -t ${WAIT_POSTGRES_TIMEOUT:-180} &&
wait-for-it -s web.local:8000 -t 300 &&
wait-for-it -s web.local:8000 -t ${WAIT_INIT_TIMEOUT:-3600} &&
./manage.py sweep_stale_tasks &&
./manage.py bg_worker -l debug -q default --autoreload
"
Expand All @@ -63,7 +66,7 @@ services:
command: >
bash -c "
wait-for-it -s postgres.local:5432 -t ${WAIT_POSTGRES_TIMEOUT:-180} &&
wait-for-it -s web.local:8000 -t 300 &&
wait-for-it -s web.local:8000 -t ${WAIT_INIT_TIMEOUT:-3600} &&
./manage.py sweep_stale_tasks &&
./manage.py bg_worker -l debug -q llm --autoreload
"
Expand Down
12 changes: 9 additions & 3 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ services:
# We can't use those manage commands inside the web container in production because
# the web service may have multiple replicas. So we make sure to only run them once
# and wait for it to be finished by the web service containers.
#
# init opens its ok_server port only after `migrate` finishes, and every
# service below waits for that port, so WAIT_INIT_TIMEOUT must stay above the
# longest migration a deploy runs -- a data migration over a large archive can
# take many minutes, and a timeout aborts the `&&` chain and exits the
# container.
Comment on lines +22 to +26
init:
<<: *default-app
hostname: init.local
Expand All @@ -43,7 +49,7 @@ services:
- ${WEB_HTTPS_PORT:-443}:443
command: >
bash -c "
wait-for-it -s init.local:8000 -t 300 &&
wait-for-it -s init.local:8000 -t ${WAIT_INIT_TIMEOUT:-3600} &&
echo 'Starting web server ...' &&
daphne -b 0.0.0.0 -p 80 \\
-e ssl:443:privateKey=/etc/web/ssl/key.pem:certKey=/etc/web/ssl/cert.pem \\
Expand All @@ -60,7 +66,7 @@ services:
command: >
bash -c "
wait-for-it -s postgres.local:5432 -t ${WAIT_POSTGRES_TIMEOUT:-180} &&
wait-for-it -s init.local:8000 -t 300 &&
wait-for-it -s init.local:8000 -t ${WAIT_INIT_TIMEOUT:-3600} &&
./manage.py sweep_stale_tasks &&
./manage.py bg_worker -q default
"
Expand All @@ -72,7 +78,7 @@ services:
command: >
bash -c "
wait-for-it -s postgres.local:5432 -t ${WAIT_POSTGRES_TIMEOUT:-180} &&
wait-for-it -s init.local:8000 -t 300 &&
wait-for-it -s init.local:8000 -t ${WAIT_INIT_TIMEOUT:-3600} &&
./manage.py sweep_stale_tasks &&
./manage.py bg_worker -q llm
"
Expand Down
Loading
Loading