Time series databases in 2026: revisit TimescaleDB, QuestDB and InfluxDB 3 Core before you store another sensor reading
Take a cold-chain company in Nashik that runs pre-cooling and cold storage chambers for export grapes and onions at four sites: MIDC Ambad, Pimpalgaon Baswant, Dindori and Niphad (an invented example, not a real client). Each site has twelve chambers. Every chamber has a temperature and humidity sensor and a door switch, wired to a small gateway that sends a reading every 30 seconds over a 4G router. A farmer-facing app shows each member the chamber where their lot is stored, and the export desk prints a temperature log for every consignment, because buyers ask for it.
Today the readings go into one ordinary PostgreSQL table, and a cron job deletes anything older than 90 days. Two things went wrong this season. The hourly report for the export desk started timing out, and a buyer questioned a log that showed twice as many “minutes above 2 °C” as the chamber could have had. The cause was dull: when 4G dropped, gateways kept their buffer and sent it again on reconnect, and the table happily stored both copies. The team (two backend developers and one operations person who also handles the routers) decides to move readings to a proper time series store. Three proposals arrive. The backend lead wants TimescaleDB, because everything else is already PostgreSQL. A developer who came from a trading firm wants QuestDB. The integrator who installed the gateways recommends InfluxDB 3 Core, because the gateways already speak line protocol through Telegraf.
This post sets the three side by side as of 20 October 2026: what identifies a reading, late data, retention and downsampling, storage formats, backup and restore, high availability, licences, security defaults and 2026 advisories. It is about application, IoT and market data queried with SQL. The earlier Revisit.Tech post on Prometheus long-term storage was a different job: infrastructure metrics whose data model and query language (PromQL) Prometheus already fixes. Here the schema is yours, and so are the mistakes. Hosted services appear only where they explain the free product.
Facts come from GitHub release and advisory APIs, licence files, official docs, CVE records, Docker Hub and a drill that ran TimescaleDB 2.30.2 on PostgreSQL 18.6, QuestDB 10.0.1 and InfluxDB 3 Core 3.12.1 on one shared box with the same invented week of sensor data. The drill checks behaviour, not speed. Licence notes are my reading of the texts, not legal advice.
The short version
- Decide what makes two readings the same before you pick a product. The same 720 re-sent rows produced 720 duplicates in TimescaleDB without a unique key and in QuestDB without
DEDUP UPSERT KEYS. InfluxDB 3 Core folded them away on its own, because a point is always identified by table, tag set and timestamp. - The licence edge in TimescaleDB is a runtime switch, not just a document. Compression (the columnstore), continuous aggregates, retention policies and background jobs live in the Timescale License code. When I set
timescaledb.license = 'apache'on a database with compressed chunks, a plainSELECT count(*)failed: “querying compressed data is not supported under the current timescaledb.license”. - InfluxDB 3 Core is built for recent data. Its default of 432 Parquet files per query is about 72 hours at the default 10-minute layout. A one-week backfill made 1,008 files, and a week-long
count(*)was refused with a pointer to Enterprise compaction. - Late data is where the quiet errors live. A TimescaleDB continuous aggregate did not show a late row until I refreshed that bucket, because real-time aggregation has been off by default since 2.13. InfluxDB 3 Core accepted rows older than the database retention period with HTTP 204 and then never returned them.
- Defaults differ sharply on security. QuestDB open source answered SQL over HTTP with no credentials and accepted the PostgreSQL wire login
admin/quest. InfluxDB 3 Core refused even/healthwithout a token, but handed out the first admin token to anyone who asked first. - Storage is close once data is columnar. For 967,680 rows, TimescaleDB went from 86 MB (rows) to 12 MB (columnstore), QuestDB Parquet was about 872 KiB a day, and InfluxDB wrote about 5.8 MB of Parquet for the week. One invented dataset, not a compression claim.
Where things stand on 20 October 2026
Release dates are GitHub published_at values converted to IST, unless a project’s own release notes give an earlier date, which I then say. Licence names come from each repository’s licence files.
TimescaleDB
- Latest: 2.30.2 (29 September 2026, 8:50 PM), a bug-fix release after 2.30.1 (17 September) and 2.30.0 (8 September, 4:24 PM). 2.30.0 added
DeferredChunkAppend, which the notes say makes the “last reading from this sensor” query constant instead of linear in chunk count when the answer is in the newest chunk (the project’s own benchmark). - PostgreSQL support: 2.29.0 (28 July 2026) removed PostgreSQL 15; current releases support PostgreSQL 16, 17 and 18. PostgreSQL 18.6 is the newest minor on postgresql.org.
- Release rhythm: 21 releases in 2026 so far, roughly one minor a month with patch releases between.
- Licence and governance: the LICENSE file says code outside the
tsldirectory is Apache 2.0 and code inside it is under the Timescale License (TSL). The built shared objects follow the same split: the package I installed shippedtimescaledb-2.30.2.soand a separatetimescaledb-tsl-2.30.2.so. The company behind it announced on 17 June 2025 that Timescale is now Tiger Data; the extension kept its name. GitHub shows the repository licence as NOASSERTION because of the split. - Attention: about 23,722 GitHub stars.
QuestDB
- Latest: 10.0.1 (24 August 2026, 7:25 PM), a maintenance release after the 10.0.0 major (6 August 2026, 9:06 PM). No release in the 57 days since.
- What 10.0 changed: QWP, a binary columnar protocol over WebSocket that the 10.0.0 notes say supersedes ILP and PostgreSQL wire for new work (both stay for compatibility), client-side store-and-forward, Live Views and a new Web Console. The notes claim tens of millions of rows per second in and over 200 million out; those are QuestDB’s own benchmarks.
- Release rhythm: 12 releases in 2026 so far.
- Licence and governance: Apache 2.0 for QuestDB Open Source. The terms page (updated 2 October 2026) says features available only in QuestDB Enterprise “are proprietary and are not licensed under the Apache License”. The project is run by QuestDB Technology Inc.
- Attention: about 17,451 GitHub stars.
InfluxDB 3 Core
- Latest: 3.12.1, dated 14 October 2026 in the release notes, with 3.12.0 on 1 October. The GitHub release objects for 3.12.1, 3.12.0, 3.11.7 and 3.11.5 were all published together on 15 October 2026 at 11:14 PM, so GitHub dates are not a reliable calendar for this project.
- What 3.12.1 fixed: among others, “acknowledged writes lost after a forced snapshot and a restart”, and overwrites lost across two snapshots. 3.12 also adds a catalog record that 3.11 cannot read, so back up
catalog/before upgrading; rollback needs it. - Release rhythm: 32 dated entries on the Core release notes page in 2026, including patch lines for 3.9, 3.10 and 3.11.
- Licence and governance: MIT or Apache 2.0, at the user’s choice (
LICENSE-MITandLICENSE-APACHEin the repository and in the release tarball). InfluxDB 3 Enterprise is a separate commercial product. The project is run by InfluxData, Inc. - Older lines: InfluxDB OSS v2.9.1 (12 May 2026) and v1.13.1 (10 September 2026) still ship from the same repository.
- Attention: about 31,772 GitHub stars, shared with the whole history of InfluxDB 1.x and 2.x.
A note on Docker tags
InfluxData’s documentation carries a banner saying that from 15 September 2026 the latest Docker tag “will point to InfluxDB 3 Core”. When I checked the Docker Hub API on 20 October 2026, influxdb:latest had the same digest as influxdb:2.9 (last updated 6 October, 9:51 AM), while influxdb:3-core and 3.12.1-core shared a different digest (17 October). Whatever the plan, an image called latest is not a version. Pin 3.12.1-core or 2.9.
Timeline
- 26 September 2013, 29 April 2014, 8 March 2017: the InfluxDB, QuestDB and TimescaleDB repositories are created, in that order.
- 24 September 2020: posted date of the current Timescale License Agreement text.
- 28 November 2023: TimescaleDB 2.13.0 makes continuous aggregates materialised-only (no real-time part) by default.
- 14 April 2025: InfluxDB 3.0.0, the first general release of InfluxDB 3 Core, per its release notes.
- 17 June 2025: Timescale announces it is now Tiger Data.
- 11 July 2025: QuestDB 9.0.0.
- 9 January 2026: QuestDB 9.3.0, which carries the Web Console fix for CVE-2026-0824.
- 3 March 2026: TimescaleDB 2.25.2 and advisory GHSA-vgp2-jj5c-828m (CVE-2026-29089, search path abuse during extension upgrade).
- 28 July 2026: TimescaleDB 2.29.0 drops PostgreSQL 15.
- 4 August 2026: TimescaleDB 2.29.1 fixes missing permission checks in internal chunk functions and decompressor crashes on malformed data.
- 6 August 2026: QuestDB 10.0.0 with QWP and Live Views; the same day, three CVE records for TimescaleDB compression decoders.
- 24 August 2026: QuestDB 10.0.1.
- 15 September 2026: the date InfluxData gave for moving the Docker
latesttag to InfluxDB 3 Core. - 29 September 2026: TimescaleDB 2.30.2.
- 1 and 14 October 2026: InfluxDB 3.12.0 and 3.12.1.
- 20 October 2026: the drill in this post, between about 2:28 AM and 2:40 AM IST.
First question: what is a reading, to your database?
“We need a time series database” usually means “our table got big and our dashboards got slow”. Both are real. But the expensive problems with sensor and market data rarely come from size. They come from three properties of every reading that the database has to understand the same way you do:
- Identity. What makes two readings the same reading? For the Nashik chambers it is (sensor, timestamp). A gateway that re-sends after a 4G drop must not create a second row. A correction to one field must replace the old value, not sit next to it.
- Arrival. How late can a reading arrive and still count? Gateways buffer through outages, so a reading can arrive hours or days after it was taken. Every downstream thing (an hourly rollup, a retention rule, a report that already ran) must still see it, or must say clearly that it did not.
- Lifetime. How long is a reading kept raw, at what resolution after that, in which format, and on which disk? Retention is not a cron job; it is a promise to a buyer, an auditor or a regulator.
Then one practical question sits above the three: which licence covers the features that keep those promises? In all three products, the free edition and the paid edition draw that line in a different place.
Before anyone installs anything, agree on one sentence:
Every reading is identified by [key columns], is accepted up to [N days] late and shows up in rollups within [N minutes] of arrival, is kept raw for [N days] and as [hourly] summaries for [N months], can be restored onto [a spare machine] within [N hours], and every feature we rely on for this is available under [licence], which we have read.
Installing any of them takes minutes. The sentence is what the next audit will check.
What each database actually is
TimescaleDB 2.30.2
TimescaleDB is a PostgreSQL extension. A hypertable looks like a normal table to your application, but it is split into chunks by time (and optionally by another column). Since the simplified DDL, you create one with plain CREATE TABLE ... WITH (tsdb.hypertable, ...). The CREATE TABLE reference says that this also creates a columnstore policy that converts chunks after one chunk interval, scheduled daily. The drill confirmed it: both test tables came up with compression_enabled = t and a job Columnstore Policy [1000] with compress_after: 1 day, matching the one-day chunks I asked for.
The columnstore (the docs also call it hypercore) stores old chunks in compressed columnar form, segmented by a column you choose (segmentby) and ordered by another (orderby). Compressed chunks still accept inserts, updates and deletes. On top sit continuous aggregates (incrementally refreshed materialised views grouped by time_bucket), retention policies (add_retention_policy), a job scheduler, and hyperfunctions such as time_bucket_gapfill, locf and approximate percentiles.
Everything else is PostgreSQL: SQL, joins, roles and row-level security, pg_dump, streaming replication and your drivers. So are the costs: row-oriented ingest until chunks convert, vacuum, connection limits, and two things to upgrade.
The editions page is a feature list that is worth printing. Under the Apache 2 edition you keep hypertables, chunks, drop_chunks, time_bucket, first, last and histogram. You lose SkipScan, the whole columnstore, continuous aggregates, add_retention_policy, add_job, time_bucket_gapfill, locf, interpolate and the approximate statistics functions. Most Linux packages ship the Community (TSL) edition by default; SHOW timescaledb.license returns timescale or apache.
QuestDB 10.0.1
QuestDB is a single server written in Java with C++ and Rust on the hot paths, shipped with its own Java runtime. Tables have a designated timestamp and are partitioned by time; data is stored by column in memory-mapped files. With the write-ahead log (WAL) enabled, the default for new partitioned tables, writes from many connections are sequenced and applied in order, and out-of-order rows are merged into the right partition.
You write over QWP, InfluxDB line protocol, PostgreSQL wire or CSV upload, and query over HTTP, PostgreSQL wire or QWP. The SQL adds SAMPLE BY for bucketing, LATEST ON ... PARTITION BY for the last row per key, ASOF JOIN for lining up two series, materialised views and Live Views. Older partitions can be converted to Parquet in place and stay queryable.
Deduplication is a table property on WAL tables: DEDUP UPSERT KEYS(ts, sensor) keeps one row per key, skips identical re-sends, and replaces the whole row when the content differs. TTL drops whole partitions once they fall outside the window.
The Enterprise comparison is blunt about the open source edition: single instance, no replication or failover, “Basic auth over unencrypted transmission”, a built-in admin user with an optional read-only user, manual full backups to local storage, and community support only. TLS, RBAC, SSO, replication with read replicas, incremental backups and point-in-time recovery, and tiered storage to object stores are Enterprise features.
InfluxDB 3 Core 3.12.1
InfluxDB 3 is a rewrite in Rust on Apache Arrow, DataFusion and Parquet. Writes arrive as line protocol (the v3 API, plus v1 and v2 compatible endpoints), go into an in-memory buffer, are flushed to a write-ahead log every second and acknowledged, and are persisted as Parquet files, by default in 10-minute blocks of data time, on local disk or object storage. Queries are SQL or InfluxQL. The migration page lists v1 and v2 write endpoints and the v1 InfluxQL query endpoint; it lists no Flux endpoint, so plan Flux rewrites if you have them.
The model is fixed in a useful way: tags identify a series, fields hold values, and a point is identified by table, tag set and timestamp. Core adds last-value and distinct-value caches and an embedded Python processing engine (its plugin library includes downsampling examples). The Core overview claims last-value queries under 10 ms; that is InfluxData’s figure.
What Core leaves to Enterprise is the important part. The overview lists, for Enterprise: “Historical query capability and single series indexing”, high availability and read replicas. The config reference explains the practical edge: --query-file-limit defaults to 432, which at the default gen1-duration of 10 minutes is about 72 hours of data, and Enterprise “optimizes data storage by compacting and rearranging Parquet files”. You can raise the limit, at the risk of slower queries, more memory and out-of-memory kills, by the docs’ own warning.
Side by side
Where no primary source exists for a point, I write “Not documented”.
Licence and governance
- TimescaleDB: Apache 2.0 outside
tsl/, Timescale License inside it. The TSL lets you run the Community edition free on your own infrastructure, modify it, and use it inside your own “Value Added Products or Services”, but forbids offering it as a database service. The definition of a value-added product requires, among other things, that its users are prevented “contractually or technically” from defining or modifying the database schema. Steward: Tiger Data (formerly Timescale, Inc.). - QuestDB: Apache 2.0 for the open source server. Enterprise features are proprietary under a subscription licence; trials are for non-production evaluation only. Steward: QuestDB Technology Inc.
- InfluxDB 3 Core: MIT or Apache 2.0. InfluxDB 3 Enterprise has Trial (30 days, 256 CPUs), At-Home (2 CPUs, no expiry, single node, not for commercial use) and Commercial licences, per the licence page. Steward: InfluxData, Inc.
- None of the three is a foundation project. In each case one company employs most maintainers and sells the edition that removes the free edition’s limits. Not legal advice; read the texts yourself.
Identity and duplicates
- TimescaleDB: no identity unless you declare one. A
UNIQUE (sensor, ts)constraint works on hypertables, including compressed chunks, but aCOPYbatch with one duplicate aborts entirely. Load into a staging table, thenINSERT ... ON CONFLICT DO NOTHING(orDO UPDATE). - QuestDB: no identity unless the WAL table has
DEDUP UPSERT KEYS. With it, identical re-sends are skipped and a changed row replaces the old row as a whole. - InfluxDB 3 Core: identity is always table plus tag set plus timestamp. A re-send folds into one point; a second write merges field sets. The line protocol reference warns that overwriting the same field “is not reliable in InfluxDB 3 Core, regardless of the delay between writes”, so do not build a last-value table on overwrites.
Late and out-of-order data
- TimescaleDB: late rows go into whichever chunk covers their time, compressed or not. Continuous aggregates are materialised-only by default since 2.13, so late rows appear in a rollup only after a refresh covers their bucket. Set the refresh policy’s
start_offsetto at least your maximum lateness, or turn on real-time aggregation (materialized_only = false), which the docs say still does not pick up new historical rows in buckets already materialised. - QuestDB: out-of-order writes are merged into the right partition through the WAL. TTL uses
min(wall clock, latest timestamp)as its reference, so one row stamped in the future cannot wipe the table (the docs describe this protection, and the drill confirmed it). - InfluxDB 3 Core: late rows are accepted, but rows older than the database retention period are filtered out at query time. The write still returns 204. A big backfill also multiplies Parquet files, because files follow data time.
Retention and downsampling
- TimescaleDB:
drop_chunks(Apache) by hand or from cron;add_retention_policyand continuous aggregates (both TSL) for automatic retention and rollups. Rollups can have their own retention. - QuestDB:
TTLon tables and materialised views in open source; Enterprise replaces table TTL with storage policies (convert to Parquet, move to object storage, then drop). Downsampling with materialised views overSAMPLE BY. - InfluxDB 3 Core: per-database retention period (minimum practical one hour,
nonefor infinite), enforced at query time and then by a deletion service. Downsampling through processing engine plugins or your own jobs; Not documented as a built-in rollup feature in Core.
Compression and storage format
- TimescaleDB: row form for recent chunks, columnar compressed form after the policy runs. The docs say “up to 98%” smaller; that is the vendor’s claim. Drill: 86 MB to 12 MB for the table without a unique index, 151 MB to 12 MB for the one with it. Data is inside PostgreSQL files, readable only through PostgreSQL with the TSL library loaded.
- QuestDB: native columnar files per partition, with optional conversion of older partitions to Parquet that stays queryable. Drill: about 4.4 MiB per closed day natively, about 872 KiB per day as Parquet.
- InfluxDB 3 Core: Parquet from the start. Drill: 1,008 files, 5,803,368 bytes for the week. Files from both QuestDB and InfluxDB began and ended with the Parquet magic bytes; I did not read them with an outside engine on this box.
Backup and restore
- TimescaleDB: PostgreSQL logical backups (
pg_dump -Fc, then restore withtimescaledb_pre_restore()andtimescaledb_post_restore()into a database with the same extension version; the docs warn againstpg_restore -j) or physical backups with the usual PostgreSQL tools. Compressed chunks are dumped compressed. - QuestDB: open source uses
CHECKPOINT CREATE, an outside copy or volume snapshot of the whole root directory, thenCHECKPOINT RELEASE; restore by placing a_restorefile in the copied root before start. Incremental backups to object storage and point-in-time recovery are Enterprise. - InfluxDB 3 Core: Not documented as a separate backup command for Core in the pages I read. The state is the object store (or data directory): WAL, catalog and Parquet. I copied the directory with the server stopped and started a second server on the copy.
High availability
- TimescaleDB: PostgreSQL streaming replication with Patroni or an operator; standbys serve reads.
- QuestDB and InfluxDB 3 Core: single node in the free edition; replication, read replicas and failover are Enterprise.
Security defaults
- TimescaleDB: whatever your PostgreSQL
pg_hba.confand roles say. Telemetry:timescaledb.telemetry_leveldefaults tobasic(I read the boot value frompg_settings). - QuestDB: HTTP binds
0.0.0.0:9000by default and has no user unless you sethttp.userandhttp.password; PostgreSQL wire defaults areadmin/quest; no TLS in open source; telemetry enabled by default. - InfluxDB 3 Core: HTTP binds
0.0.0.0:8181by default, token authentication on, TLS available with--tls-certand--tls-key, telemetry upload on unless--disable-telemetry-upload. The first admin token can be created without authentication; an--admin-token-filelets you preconfigure it.
Footprint observed
- Minimum hardware: Not documented in the pages I read, for all three.
- Drill (one run, small data): PostgreSQL with TimescaleDB and
shared_buffers = 256MB, about 143 MiB RSS in total; QuestDB with-Xmx768m(its script pre-touches the heap), about 586 MiB; InfluxDB 3 Core with a 512 MB query pool, about 528 MiB after ingest and 144 MiB after a restart.
Worked example for the Nashik scenario
The Nashik team (invented) did the following before choosing.
Step 1. Write the sentence with numbers. Identity: (sensor, timestamp). Lateness: gateways buffer up to 72 hours, so a reading may arrive three days late. Rollups: hourly, visible within 15 minutes of arrival. Lifetime: raw readings for 400 days (one export season plus margin), hourly summaries for three years. Restore: on a spare VM at the Ambad office within four hours. All of these numbers are invented for the example; yours will come from your buyers and auditors.
Step 2. Hold each product’s defaults against the sentence. InfluxDB 3 Core’s default 72-hour query window clashed with season-long consignment reports, and compaction is Enterprise. QuestDB open source fitted identity, lateness and lifetime well, but the buyer’s audit wanted named accounts and encrypted links, which means QuestDB Enterprise. TimescaleDB fitted all three, with one homework item: the licence.
Step 3. Read the licence for the actual product. Their farmer app and export desk are the product; members never define tables. On their reading, self-hosting the Community edition for their own application fits the TSL’s value-added terms, and they recorded that reasoning, with clause numbers, for their lawyer to confirm. Not legal advice.
Step 4. Make identity part of the schema, and make gateway batches idempotent. Adapted from the drill:
CREATE TABLE readings (
ts timestamptz NOT NULL, sensor text NOT NULL, site text NOT NULL,
temp_c double precision, rh_pct real, door_open boolean,
UNIQUE (sensor, ts)
) WITH (tsdb.hypertable, tsdb.partition_column = 'ts',
tsdb.chunk_interval = '1 day',
tsdb.segmentby = 'sensor', tsdb.orderby = 'ts DESC');
-- each gateway batch: stage, then insert once
CREATE TEMP TABLE stage (LIKE readings);
\copy stage FROM 'batch.csv' CSV HEADER
INSERT INTO readings SELECT * FROM stage ON CONFLICT (sensor, ts) DO NOTHING;
Step 5. Cover lateness in the rollup, not only in the table. An hourly continuous aggregate with a refresh policy whose start_offset is four days (one more than the gateway buffer), end_offset of 15 minutes and a 15-minute schedule. The export desk report reads the aggregate; the farmer app, which wants the latest value, reads the raw table with DISTINCT ON (sensor).
Step 6. Write lifetime as policies. The columnstore policy left at its default (one chunk interval, so one day), a retention policy of 400 days on readings, and a separate three-year retention on the aggregate. A monitoring query checks SHOW timescaledb.license and alerts if it ever reads apache, because compressed chunks would then be unreadable.
Step 7. Restore before trusting. A nightly pg_dump -Fc goes to the backup host with mode 0600, plus physical backups and WAL archiving as for any PostgreSQL. Once a month the operations person restores the dump into a fresh database on the spare VM, with the same TimescaleDB version, and compares row counts, aggregate sums and the newest timestamp per site.
Security and hygiene
Each GitHub advisory was read in full, and each CVE ID was checked against its record on cveawg.mitre.org. Times are IST. A short list can mean fewer bugs, fewer reporters or less disclosure; TimescaleDB also runs inside PostgreSQL as a superuser-installed extension, so its bugs sit next to privilege.
TimescaleDB
- GHSA-vgp2-jj5c-828m, CVE-2026-29089 (advisory 3 March 2026, 6:42 PM; CVE record 6 March, 10:36 PM; CVSS 3.1 8.8): in 2.23.0 to 2.25.1, the extension upgrade script resolved unqualified names through
search_path, so a user who could write to a schema on that path could shadow built-in functions and run code as superuser duringALTER EXTENSION ... UPDATE. Fixed in 2.25.2. The advisory’s workaround is to block extension upgrades until patched, and says Tiger Cloud and the managed service were not exploitable. - GHSA-hcfx-29v5-2rcw (6 August 2026, 5:37 PM; high, 8.1; no CVE ID): internal chunk-management functions in
_timescaledb_functionslacked owner checks, so any database user could drop or freeze chunks of hypertables they did not own. Affects 2.7.0 to 2.29.0; fixed in 2.29.1. Workaround: revokeEXECUTEfromPUBLICon those internal functions. - CVE-2026-70633, CVE-2026-70634, CVE-2026-70635 (all 6 August 2026, from about 10:21 PM; CVSS 4.0 scores 7.1, 7.2 and 7.1; filed by VulnCheck): out-of-bounds reads in the Gorilla, Dictionary and Simple8b decoders, reachable by an authenticated user who stores crafted compressed data. The records say “through 2.29.1” and point to the fix commit and PR 10360, which the 2.29.1 release notes list as “Fix decompressor crashes with malformed compressed data”. To be safe, treat 2.29.2 or later as fixed. This is TSL code (
tsl/src/compression).
QuestDB
- CVE-2026-0824 (10 January 2026, 8:02 PM; VulDB; CVSS 3.1 3.5, CVSS 4.0 5.1): cross-site scripting in the Web Console (the
questdb/uicomponent up to 1.11.9). The record quotes the vendor saying the fix ships in QuestDB 9.3.0, released the day before. - No GitHub advisories in the repository. The larger risk in practice is the default posture, not a CVE: in the drill, an unauthenticated
POST /writecreated a new table, and an unauthenticatedGET /execran SQL.
InfluxDB 3 Core
- No GitHub advisories in the repository, and the NVD keyword search for “influxdb” found no 2026 CVE for InfluxDB itself (the one match was another product). The 3.12.1 notes do list data-loss fixes (acknowledged writes lost after a forced snapshot and restart), which matter more to an IoT team than many CVEs would.
Habits that matter more than the counts:
- Bind to a private address and put TLS in front. QuestDB open source has no TLS; InfluxDB has it but binds every interface by default.
- Change default credentials on day one: QuestDB’s
admin/quest, and sethttp.user/http.password. - Create InfluxDB’s admin token at install time, or use
--admin-token-file, and store it like a root password. - Upgrade extensions as a clean superuser session with a fixed
search_path, and follow the TimescaleDB advisory workaround of revokingEXECUTEon internal functions fromPUBLIC. - Treat dumps and copied data directories as sensitive. My
pg_dumpfile came out mode 0644 under the default umask. - Decide on telemetry explicitly. All three send some usage data by default; each has a documented switch.
Decision guide
Lean towards TimescaleDB if
- Your readings must join with relational data (lots, consignments, customers, devices), and your team already runs PostgreSQL well.
- You need declared identity with unique constraints, transactions and PostgreSQL roles and row-level security.
- You can live with the TSL for the features you will actually use (compression, rollups, retention policies), or you are happy with the Apache subset and your own cron jobs.
Lean towards QuestDB if
- Ingest rate and time series SQL (
SAMPLE BY,LATEST ON,ASOF JOIN) matter most, as with market data, telemetry at high rates or trading tools. - Deduplication as a table property and TTL that is safe against future timestamps fit your model.
- A single instance with manual checkpoint backups is acceptable, or you plan to buy Enterprise for TLS, RBAC and replication.
Lean towards InfluxDB 3 Core if
- Your queries are mostly about the last hours or days: live dashboards, alerting, last-value lookups.
- You already have line protocol and Telegraf everywhere, and you want permissive licences (MIT or Apache 2.0) with Parquet files on object storage.
- You are comfortable that long-range history needs either another engine reading the Parquet files, a higher file limit with care, or InfluxDB 3 Enterprise.
Be careful whichever you pick
- Duplicates from retries are a data quality bug, not a storage detail. Test them.
- Rollups and retention must know about lateness. Test a late row.
- A restore that was never run is a hope. Run it.
Nashik (invented) pick. The team chose TimescaleDB 2.30.2 on PostgreSQL 18.6, Community edition, self-hosted: a unique (sensor, ts) key with staged inserts, one-day chunks and the default columnstore policy, an hourly continuous aggregate refreshed every 15 minutes over a four-day window, 400-day raw retention and three-year rollup retention, nightly dumps plus physical backups, and a monthly restore on the spare VM. Reasons: the joins with consignment and member tables, the existing PostgreSQL skills, and the fact that every promise in their sentence could be expressed as a constraint or a policy. QuestDB was the developer’s favourite and stays on the list for a planned buyer-facing live dashboard, if the company later buys Enterprise for TLS and accounts. InfluxDB 3 Core was ruled out for the system of record only because of the long-range reports, not on quality.
Checklist
- Write the one-sentence promise: identity, lateness, rollup freshness, raw and rollup lifetime, restore time, licence.
- Declare identity in the database (unique key,
DEDUP UPSERT KEYS, or tag design) and test a re-sent batch. - Make batch loads idempotent: staging plus
ON CONFLICT, or a deduplicating table. - Insert a late row on purpose and check every rollup, report and retention rule.
- Check which licence covers each feature you rely on, and record it.
- Set retention as a policy, with separate lifetimes for raw data and rollups.
- Measure storage on your own data before and after compression or Parquet conversion.
- Back up, then restore onto a clean machine and compare counts, sums and newest timestamps.
- Close the network: private binds, TLS, no default credentials, tokens stored as secrets.
- Pin versions (and Docker tags), read release notes for data-loss fixes, and patch on a schedule.
Common mistakes
- Trusting gateways not to re-send.
- Loading with
COPYinto a table with a unique key and losing the whole batch on one duplicate. - Assuming a continuous aggregate includes rows that arrived after its last refresh.
- Switching TimescaleDB to the Apache edition on a database that already has compressed chunks.
- Backfilling a week into InfluxDB 3 Core and then querying the whole week.
- Writing old data into an InfluxDB database whose retention is shorter than the data’s age, and reading the 204 as success.
- Leaving QuestDB’s HTTP port open on a network with no
http.userset, or keepingadmin/quest. - Relying on InfluxDB overwrites to keep a last-value view.
- Running
pg_restore -jon a TimescaleDB dump, or restoring into a different extension version. - Pulling
influxdb:latestand assuming you know which major you got.
Drill on this box, 20 October 2026
This machine is a shared Debian 13 VM with 8 vCPUs and 15 GiB RAM (about 4 GiB free, other work running) and no Docker. PostgreSQL 18.6 and TimescaleDB 2.30.2 were unpacked from Debian packages, QuestDB 10.0.1 and InfluxDB 3 Core 3.12.1 from release tarballs; every download matched its published SHA-256. Each server listened only on 127.0.0.1 on non-default ports and ran alone: TimescaleDB about 2:28 to 2:30 AM IST, QuestDB 2:31 to 2:36 AM, InfluxDB 2:36 to 2:40 AM.
The data was invented: 48 chambers, one reading every 30 seconds from 12 to 18 October 2026 (UTC), 967,680 unique rows, split into 959,040 rows in order, 8,640 “late” rows (six hours of the Niphad gateway, five days back) and 720 “re-sent” Dindori rows that were already present. TimescaleDB got CSV, the other two line protocol. Single runs; no number here is a speed claim.
TimescaleDB 2.30.2. Two hypertables with one-day chunks, readings and readings_keyed with UNIQUE (sensor, ts), both created with a columnstore policy. Row form: 86 MB and 151 MB; after compress_chunk on all seven chunks, 12 MB each. Late rows went into compressed chunks without complaint. Re-sent rows: readings grew to 968,400 with 720 duplicate pairs; the COPY into readings_keyed failed on its first line (duplicate key value violates unique constraint), and the staged ON CONFLICT DO NOTHING inserted 0, leaving 967,680. An hourly continuous aggregate held 8,064 rows. One late 9.9 °C row in an already materialised hour left the aggregate at max 0.81, n 120 until I refreshed that day, then max 9.9, n 121. pg_dump -Fc wrote 11,905,885 bytes (mode 0644); a restore with the pre and post restore functions gave the same counts, 12 compressed chunks and both policy jobs.
Then the licence switch. With timescaledb.license = 'apache' and a restart:
SELECT count(*) FROM readings_keyed;
ERROR: querying compressed data is not supported under the current "timescaledb.license"
DETAIL: Chunk "_hyper_2_8_chunk" is compressed and requires the TimescaleDB Community Edition to query.
In that mode a new hypertable came up with compression disabled, add_retention_policy and enabling the columnstore were refused, while create_hypertable, drop_chunks, time_bucket and reading the existing aggregate worked. Resetting the setting brought everything back.
QuestDB 10.0.1. Two WAL tables partitioned by day, readings and readings_dedup with DEDUP UPSERT KEYS(ts, sensor). After all three files: 968,400 rows with 720 duplicate pairs in readings, 967,680 in readings_dedup, late rows in the right partition. Closed days were 4.4 MiB each; CONVERT PARTITION TO PARQUET on five days made them about 872 KiB each, and counts did not change. SET TTL 3 DAYS dropped three partitions at once, measured from the newest row; a row for 19 October dropped one more; a row stamped 1 January 2027 dropped nothing. CHECKPOINT CREATE, cp -a of the root (540 MB), CHECKPOINT RELEASE, then five more rows on the original: an instance started from the copy with a _restore file had 967,680 rows and its Parquet partitions. Defaults as shipped: SQL over HTTP with no credentials worked, an unauthenticated write created a new table, and psql as admin/quest connected.
InfluxDB 3 Core 3.12.1. File object store, 512 MB query pool. Before any token existed, /health returned 401; influxdb3 create token --admin then worked with no credentials, and a second attempt got 409. After all three files: 967,680 rows and no duplicate (sensor, time) pairs. Writing only temp_c=7.77 for an existing point changed that field and kept the others. A restart with --force-snapshot-max-age 1m persisted exactly 1,008 Parquet files, one per 10-minute block of data time. Then:
influxdb3 query -d cold "select count(*) from readings"
Error: Query would scan 432 Parquet files, exceeding the file limit. ...
To remove this limitation, upgrade to InfluxDB 3 Enterprise, ...
The number in the message is the limit; the week had 1,008 files. From 16 October (432 files) the count worked: 414,720. A database with --retention-period 1d accepted three rows dated 12 October with HTTP 204 and then counted 0. A copy of the stopped server’s directory, started on another port, returned the same counts with the same token.
What this box could not test: replication or failover (Enterprise for QuestDB and InfluxDB; no PostgreSQL standby set up), QWP clients and store-and-forward, InfluxDB’s Python engine and S3, TimescaleDB jobs on their own schedule (I ran conversions and refreshes by hand), retention over real days, version upgrades, outside Parquet readers, sustained load and any speed comparison. All drill processes were stopped by about 2:40 AM IST; nothing else on the box was touched.
Unlearn
- Unlearn that a time series database is just a faster table.
- Unlearn that a retry is harmless.
- Unlearn that a rollup sees everything the table sees.
- Unlearn that “open source” describes every feature you use.
- Unlearn that a 204 means your data is queryable.
Re-learn
- Re-learn the three properties of a reading: identity, arrival, lifetime.
- Re-learn your licence edge, feature by feature.
- Re-learn
ON CONFLICT,DEDUP UPSERT KEYSand tag design as tools for the same job. - Re-learn what your rollups do with late rows.
- Re-learn your restore: a spare VM, once a month, counts compared.
Closing
Time series databases in 2026 differ less in raw storage than in what they assume about your readings. TimescaleDB 2.30.2 brings PostgreSQL’s constraints, joins and tools, with the useful automation behind a licence switch that you should understand before you depend on it. QuestDB 10.0.1 brings fast time series SQL, deduplication and safe TTL in one Apache-licensed server, and leaves security and replication to Enterprise. InfluxDB 3 Core 3.12.1 brings permissive licences, Parquet and fixed series identity, and is honest that long history needs Enterprise or another reader. The post on Prometheus long-term storage covered metrics whose shape Prometheus already decides; here the shape is your own, and if you pick TimescaleDB, the post on PostgreSQL backups is your next read. Learn the three properties. Unlearn “just store it”. Re-learn your late rows and your restore. Practise them. Apply them before you store another sensor reading.
Sources
TimescaleDB
- timescale/timescaledb on GitHub
- TimescaleDB 2.30.2 release
- TimescaleDB 2.30.0 release
- TimescaleDB 2.29.1 release
- TimescaleDB 2.29.0 release (PostgreSQL 15 removed)
- TimescaleDB 2.13.0 release (materialised-only default)
- TimescaleDB LICENSE (Apache 2.0 and TSL split)
- Timescale License Agreement
- Compare TimescaleDB editions
- CREATE TABLE for hypertables
- add_columnstore_policy()
- Real-time aggregates
- Logical backup with pg_dump and pg_restore
- Timescale becomes Tiger Data (17 June 2025)
- PostgreSQL versioning policy
QuestDB
- questdb/questdb on GitHub
- QuestDB 10.0.1 release
- QuestDB 10.0.0 release
- QuestDB LICENSE (Apache 2.0)
- QuestDB terms
- QuestDB Enterprise feature comparison
- Deduplication
- Time To Live (TTL)
- Backup and restore
- HTTP server configuration
- PostgreSQL wire protocol configuration
- Telemetry configuration
InfluxDB 3 Core
- influxdata/influxdb on GitHub
- InfluxDB 3.12.1 release
- InfluxDB 3 Core release notes
- InfluxDB 3 Core overview
- InfluxDB 3 Core configuration options
- Line protocol reference (duplicate points)
- Data retention in InfluxDB 3 Core
- InfluxDB 3 Core internals and durability
- Manage admin tokens
- Upgrade InfluxDB
- Migrate from InfluxDB v1 or v2
- InfluxDB 3 Enterprise licences
- LICENSE-MIT and LICENSE-APACHE
- InfluxDB on Docker Hub
Security records
- TimescaleDB security advisories
- GHSA-vgp2-jj5c-828m
- GHSA-hcfx-29v5-2rcw
- CVE-2026-29089 on cve.org
- CVE-2026-70633 on cve.org
- CVE-2026-70634 on cve.org
- CVE-2026-70635 on cve.org
- TimescaleDB PR 10360 (decompressor fixes)
- QuestDB security advisories
- CVE-2026-0824 on cve.org
- InfluxDB security advisories
Internal
Releted Posts
Self-hosted search in 2026: revisit Meilisearch, Typesense and Manticore Search before you add another search box
Picture an electrical and hardware distributor headquartered in Vadodara, with warehouses in Anand, Bharuch, Godhra, Rajkot and Surat (an invented example, not a real client).
Read moreMongoDB alternatives in 2026: revisit Percona Server for MongoDB, FerretDB and DocumentDB before you migrate another collection
Picture a diagnostic lab chain headquartered in Guwahati, with collection centres in Shillong, Jorhat, Silchar, Dibrugarh and Tezpur (an invented example, not a real client).
Read moreLog storage in 2026: revisit Grafana Loki, VictoriaLogs and OpenObserve before you keep another month of logs
Picture a platform team in Pune that runs a B2B lending stack for NBFCs across Maharashtra and Gujarat (an invented example, not a real client).
Read more