SynapCores v1.17.0 — your indexes now actually get used
What changed
Indexed lookups got fast, because they were never being used
If your query had a LIMIT — which almost every application puts on a point lookup — the index was ignored and the whole table was scanned. On a real deployment, one lookup against a 189,000-row table:
SELECT * FROM zip_fips_crosswalk WHERE zip_code = '19103' LIMIT 1;
-- before: 8,700 ms
-- after: 1.6 ms
The index was built correctly, stored durably, and maintained through updates and deletes. The planner simply never chose it. Every release that shipped an index fix made the index's contents more correct; none of them checked whether a query got faster with an index than without. That check now runs on every release.
Also fixed in the same area: EXPLAIN reported IndexScan for queries the engine actually ran as a table scan. If you used EXPLAIN to decide whether your index was working, it was telling you yes when the answer was no.
A range query on an indexed column returned rows that don't match
This one is a correctness fix, and it affects v1.16.0-ce:
SELECT n FROM t WHERE n BETWEEN 21223 AND 21263; -- indexed column
-- v1.16.0-ce: 1000 rows, 997 of them OUTSIDE the range
-- v1.17.0-ce: 123 rows, all correct
SELECT COUNT(*) FROM t WHERE n BETWEEN 21223 AND 21263;
-- 123 on BOTH versions
So the rows and the count disagreed with each other, with no error. If you run bounded range queries (BETWEEN, or > and < together) against an indexed column, results before this release could be wrong. Upgrading fixes it; no data was damaged, the reads were wrong.
AI functions stop inventing answers
When the text-generation model failed to load, GENERATE returned a sentence that looked like an answer:
"I understand you're asking about '...'. The GGUF model is being loaded. Please ensure the model file exists in the models directory."
That came back as a successful row. A caller could not tell it from a real completion. It now returns an error.
Column sizes and decimal scale are kept
VARCHAR(100)stayedVARCHAR(100)instead of silently becoming 255.CHAR(n)likewise.information_schemareports what you declared.CAST(x AS DECIMAL(10,2))applies the scale you asked for:CAST(12.5 AS DECIMAL(10,2))is now12.50.
Rename a table
ALTER TABLE orders RENAME TO archived_orders;
RENAME TABLE archived_orders TO orders_2026;
Rows, constraints, defaults and index identity survive the rename and a restart. The destination must not exist, and the source must. Not supported, and refused before anything is written: cross-database moves, columnar/external/partitioned/immutable tables, tables inside an active transaction, auto-increment columns, foreign keys, dependent views, and tenants holding triggers, stored procedures or durable agents.
Storage maintenance you can actually run
POST /v1/admin/vacuum, and offline synap --database /path admin vacuum --full, now flush and compact the live stores and report measured per-store results. Note what this can and cannot do: compaction reclaims obsolete versions, it does not shrink live data.
The model store also stopped leaving a backup copy behind on every lock recovery — one deployment had accumulated 395 of them, 203 MB.
Faster, and a cache that was going to the wrong place
The configured cache was being given to RocksDB's row cache, which only helps re-reading the same key, while the block cache that serves random reads sat at an 8 MB default and there was no bloom filter. On network-backed storage that halved an indexed lookup, 36 ms to 15 ms.
Every row-storage query shape we track is 2.4–7.9% faster than v1.16.0-ce. Parquet read performance against DuckDB is unchanged at 0.80× median.
Fixes at a glance
- Secondary indexes ignored whenever the query had a
LIMIT - Bounded range predicates on an indexed column returned rows that don't match
EXPLAINdescribed a plan the engine didn't runGENERATEreturned a fabricated answer instead of an error on model-load failureVARCHAR(n)/CHAR(n)declared lengths discardedCAST(... AS DECIMAL(p,s))ignored the scale- Model store leaked a backup directory on every lock recovery
- Admin vacuum did not reach live stores
Upgrading
Drop-in. No migration, no configuration change.
If you relied on bounded range queries over indexed columns, re-run anything whose output you kept — those results were wrong before this release.
Known limitations
- These do not use an index yet, and perform as a full scan:
IN (...)lists,ORof two indexed equalities, bare bounded ranges without aLIMIT, top-N (ORDER BY indexed_col LIMIT k), and JOIN keys. Details and measurements indocs/qa/index_coverage_gaps.md. LIKE 'prefix%'is slow — about 19.9 s over 150,000 rows, the same as in v1.16.0-ce.- An indexed point lookup fetches the row from disk, so on network-backed storage expect tens of milliseconds rather than single digits.
CAST(x AS DECIMAL(p,s))no longer ignores scale, but rescaling an existingDECIMALstill isn't possible — declare the column at the scale you want.
Install
curl -fsSL https://get.synapcores.com/install.sh | sh
Docker:
docker run -d --name synapcores -p 8080:8080 \
-e AIDB_ACCEPT_LICENSE=1 synapcores/community:v1.17.0-ce
Linux x86_64 and aarch64 (glibc 2.31+ and Ubuntu 24.04 builds), macOS aarch64, and multi-arch Docker images on GHCR and Docker Hub. See https://docs.synapcores.com for setup.