Follow-up to #22 / #26.
Context
After enabling reWriteBatchedInserts=true in #26 the WMATA import dropped from ~10 min to ~4m26s on a fresh docker compose stack. The block-write phase (the hot loop in DbWriter.actuallyWriteData at transitclock/src/main/java/org/transitclock/gtfs/DbWriter.java:129) is still the dominant cost — ~266s of the 266s total.
Observation
With hibernate.jdbc.batch_size=50 set in docs/examples/postgres_hibernate.cfg.xml, Postgres log_statement=mod shows the cascade-saved tables arriving as 32-row multi-row INSERTs, not 50-row. Examples from the import:
TravelTimesForStopPaths (9 cols): $1…$288 → 32 rows
Trips (17 cols): $1…$544 → 32 rows
Block_to_Trip_joinTable (7 cols): $1…$224 → 32 rows
PG's only hard ceiling is 32,767 bound parameters per statement (~3,400 rows for a 9-col table, ~1,900 for a 17-col table), so the 32-row chunks are nowhere near it. The most likely explanations are some interaction between Hibernate's batch_size, DbWriter's session.flush() cadence (transitclock.db.batchSize = 100), and pgjdbc's batch rewriter — but I haven't actually traced it.
Suggested experiment
- Bump
hibernate.jdbc.batch_size in docs/examples/postgres_hibernate.cfg.xml to 100, then 200.
- Re-run
./quickstart.sh --gtfs-zip /path/to/wmata.zip --avl-url … from a clean state.
- Compare the
DbWriter — Finished writing GTFS data to database. Took N msec line, plus per-1000-block timings.
- Confirm the multi-row INSERT shape with
docker compose logs db | grep 'insert into Trips' (after enabling log_statement=mod).
If the chunk size in PG actually grows to 100+, expect a meaningful per-batch fixed-cost reduction (parse, bind, network). If it stays pinned at 32, that tells us the limit is somewhere else (probably in pgjdbc's rewriteBatchedInserts chunking or in DbWriter's flush cadence) and the next investigation is to lift that.
Notes
- The current value of 50 was chosen so each
transitclock.db.batchSize=100 flush ships a whole number of JDBC batches; if we bump batch_size, consider whether transitclock.db.batchSize should track it.
- Live AVL/AD writes via
DataDbLogger also use hibernate.jdbc.batch_size. A larger batch is probably fine for them too (those rows are tiny), but worth a quick smoke under load before merging.
Follow-up to #22 / #26.
Context
After enabling
reWriteBatchedInserts=truein #26 the WMATA import dropped from ~10 min to ~4m26s on a fresh docker compose stack. The block-write phase (the hot loop inDbWriter.actuallyWriteDataattransitclock/src/main/java/org/transitclock/gtfs/DbWriter.java:129) is still the dominant cost — ~266s of the 266s total.Observation
With
hibernate.jdbc.batch_size=50set indocs/examples/postgres_hibernate.cfg.xml, Postgreslog_statement=modshows the cascade-saved tables arriving as 32-row multi-row INSERTs, not 50-row. Examples from the import:TravelTimesForStopPaths(9 cols): $1…$288 → 32 rowsTrips(17 cols): $1…$544 → 32 rowsBlock_to_Trip_joinTable(7 cols): $1…$224 → 32 rowsPG's only hard ceiling is 32,767 bound parameters per statement (~3,400 rows for a 9-col table, ~1,900 for a 17-col table), so the 32-row chunks are nowhere near it. The most likely explanations are some interaction between Hibernate's
batch_size,DbWriter'ssession.flush()cadence (transitclock.db.batchSize= 100), and pgjdbc's batch rewriter — but I haven't actually traced it.Suggested experiment
hibernate.jdbc.batch_sizeindocs/examples/postgres_hibernate.cfg.xmlto 100, then 200../quickstart.sh --gtfs-zip /path/to/wmata.zip --avl-url …from a clean state.DbWriter — Finished writing GTFS data to database. Took N msecline, plus per-1000-block timings.docker compose logs db | grep 'insert into Trips'(after enablinglog_statement=mod).If the chunk size in PG actually grows to 100+, expect a meaningful per-batch fixed-cost reduction (parse, bind, network). If it stays pinned at 32, that tells us the limit is somewhere else (probably in pgjdbc's
rewriteBatchedInsertschunking or inDbWriter's flush cadence) and the next investigation is to lift that.Notes
transitclock.db.batchSize=100flush ships a whole number of JDBC batches; if we bumpbatch_size, consider whethertransitclock.db.batchSizeshould track it.DataDbLoggeralso usehibernate.jdbc.batch_size. A larger batch is probably fine for them too (those rows are tiny), but worth a quick smoke under load before merging.