| Warning |
|---|
DRAFT v10.1.5. Pre-release draft for content review. Do not link from public material. The final page replaces this draft once 10.1.5 ships. |
| Page properties | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
|
| Info |
|---|
New in 10.1.5. QuestDB is integrated across three FrameworX data connection surfaces: Device Protocol, UNS TagProvider, and Historian StorageLocation. QuestDB speaks the PostgreSQL wire protocol natively, so SQL queries against historized data work through the existing PostgreSQL Dataset Provider with one connection-string flag - no separate QuestDB Dataset Provider is needed. |
QuestDB time-series databases for high-throughput industrial telemetry.
| Table of Contents | ||||||
|---|---|---|---|---|---|---|
|
QuestDB is a high-performance open-source time-series database designed for industrial telemetry, financial market data, and IoT workloads. FrameworX integrates QuestDB across three runtime surfaces, plus a fourth path for SQL Dataset queries through the existing PostgreSQL provider.
Surface | Configure in | What it does |
|---|---|---|
Device Protocol | Devices / Channels / Protocol = | Map QuestDB table columns as Channel, Node, and Point tags. Reads pull the latest row of a table, writes update the latest row through ILP append. |
UNS TagProvider (TagDiscovery) | UNS / TagProviders / Protocol = | Browse QuestDB tables as a tag tree. Register columns as UNS tags. Updates write back via ILP. |
Historian StorageLocation | Historian / Storage Locations / Protocol = | Append historian tag samples to QuestDB through ILP/HTTP. High-throughput batched writes. Reads via PostgreSQL wire range queries. |
Dataset Provider (existing PostgreSQL) | Datasets / DBs / Provider = | Run native SQL |
A QuestDB UnsTagProvider row and a Historian StorageLocation row can both point at the same QuestDB instance.
.NET 10 host required for ILP writes. The official QuestDB .NET client net-questdb-client targets .NET 6 and newer only - it has never shipped a .NET Framework or .NET Standard 2.0 build. FrameworX hosts ILP writes from TServer.NET10.exe (Windows, Linux, or Docker on .NET 10).
The .NET Framework 4.8 host (TServer.exe) loads the QuestDB connector but cannot perform ILP writes. Connection attempts fail-fast at connect time with a clear FileNotFoundException("net-questdb-client.dll not found"). PostgreSQL-wire reads still work on net48 because Npgsql 4.0.1 supports both targets. Use a Multiplatform / .NET 10 solution target when QuestDB ILP writes are needed.
QuestDB .NET clients ship unconditionally with FrameworX in FS/ThirdPartyBin/net10.0/: net-questdb-client for ILP ingestion (Apache 2.0, ~91 KB) and Npgsql 4.0.1 for PostgreSQL-wire queries (PostgreSQL License BSD-style, ~736 KB). No separate install and no optional pack.
Install a QuestDB server, then configure FrameworX to point at it. QuestDB is OS-native and has no FrameworX dependency on the server side.
Follow the official QuestDB installation guide at questdb.com/docs/get-started. Summary by target:
docker run -d --name fx-questdb -p 8812:8812 -p 9000:9000 questdb/questdb:latest.rt-windows distribution (bundled JRE) and run the included questdb.exe service installer.brew install questdb && brew services start questdb.Run the command below to confirm the server responds on the PostgreSQL wire port (8812):
| Code Block | ||
|---|---|---|
| ||
psql "host=localhost port=8812 user=admin password=quest dbname=qdb" -c "SELECT 1 AS ping" |
And the ILP/HTTP port (9000):
| Code Block | ||
|---|---|---|
| ||
curl -sS "http://localhost:9000/exec?query=SELECT%201%20AS%20ping" |
A successful ping returns a row containing 1. Default credentials on a fresh local install are admin / quest on database qdb.
QuestDB tables are created on first write through ILP. To pre-create a starter table for testing, run the following statement through the QuestDB web console (port 9000) or via psql:
| Code Block | ||
|---|---|---|
| ||
CREATE TABLE sensors ( timestamp TIMESTAMP, plant SYMBOL, temperature DOUBLE, quality SHORT ) TIMESTAMP(timestamp) PARTITION BY DAY; INSERT INTO sensors VALUES (now(), 'Plant01', 22.3, 192); |
Use qdb as the database in the FrameworX connection string in the next section, or use the database name you configured for your QuestDB install.
All four QuestDB surfaces (Devices, UNS TagProvider, Historian, and the PostgreSQL Dataset path) share the same Station / connection string format. The Designer Station editor exposes structured fields; the persisted format is semicolon-delimited.
| Code Block | ||
|---|---|---|
| ||
Host;PgPort;IlpEndpoint;Username;Password;Database;TlsEnabled |
Default values:
| Code Block | ||
|---|---|---|
| ||
localhost;8812;http://localhost:9000;admin;quest;qdb;false |
Field | Required | Description | Example |
|---|---|---|---|
Host | Yes | Host name or IP of the QuestDB server. | localhost |
PgPort | Yes | PostgreSQL wire protocol port. Default is 8812. Used for read-side queries (UNS browse, Historian range queries, Device polling). | 8812 |
IlpEndpoint | Yes | Full URL of the ILP/HTTP ingestion endpoint. Default is | http://localhost:9000 |
Username | No | Database user. Default for fresh local installs is | admin |
Password | No | User password. Stored encrypted in the solution file. Default for fresh local installs is | ******** |
Database | Yes | Target database name. Default is | qdb |
TlsEnabled | No | Set to true for TLS/SSL connections on the PostgreSQL wire port. ILP TLS is planned for a later release. | false |
Use the Device Protocol surface when you want each QuestDB column addressed as an individual Tag with a Node-level connection. This is the classic SCADA Channel / Node / Point model.
Use UNS TagProvider instead (next section) when you want dynamic browse and tree-style registration.
Channel. Devices / Channels / New. Protocol = QuestDB. Interface is automatically Custom (no CommAPI).
Node. One Node per QuestDB server plus database. PrimaryStation uses the standard QuestDB Station format documented in the Connection string section above.
Example value:
| Code Block | ||
|---|---|---|
| ||
localhost;8812;http://localhost:9000;admin;quest;qdb;false |
Point. Address is a single-dot Table.Column string. Example: sensors.temperature.
On each scan cycle, the driver runs one query per configured address over the PostgreSQL wire protocol, ordered by the table's designated timestamp column descending and limited to 1, projecting the requested column. Quality is 192 (Good) on hit, 0 (Bad) when the table is empty or the column is absent.
Values round-trip as strings in the Device path (matching the MongoDB connector convention). Numeric or boolean tags show their value as text. Use the UNS TagProvider surface (next section) when your integration needs the value native type preserved, or the Dataset Provider section for typed query result sets.
WriteTagValue sends an ILP/HTTP line to the IlpEndpoint, appending a row to the table with the current timestamp. Unlike the MongoDB connector, this is append-style behavior even on the Device surface - QuestDB's storage model is append-only by design. Use the Historian StorageLocation surface (later section) for batched-append behavior optimized for high-volume historian writes.
Only single-dot addresses are accepted in 10.1.5. Table.Column works. Schema.Table.Column is rejected. Schema-level addressing is planned for a later release.
This example maps a single QuestDB column to a Tag using the Channel / Node / Point model. Prerequisites: a running QuestDB server with the sensors table from the Installation section above, plus a UNS Tag named SensorTemp of type Double.
QuestDB. The Interface is set to Custom automatically.| Code Block | ||
|---|---|---|
| ||
localhost;8812;http://localhost:9000;admin;quest;qdb;false |
sensors.temperature.temperature value from the most recent row in the sensors table on each scan cycle.@Tag.SensorTemp = 75.0;. The driver appends an ILP line to the sensors table with the current timestamp.Use the UNS TagProvider surface when you want to browse a QuestDB instance from the Designer UNS tree, discover column names dynamically, and register columns as UNS tags.
UNS / TagProviders / New. Protocol = QuestDB. Station uses the same delimited format as the Device Protocol and the Connection string section above.
The browse view has two levels:
SHOW TABLES.Designer refresh does not mutate state. Browse is side-effect-free and safe to call on every tree expansion.
Select a column under a table and use Add to UNS. The registered address is Table.Column. The attribute type is mapped from the QuestDB column type:
QuestDB column type | FrameworX template name |
|---|---|
| Boolean |
| Integer |
| Long |
| Double |
| Text |
| DateTime |
| Text (string fallback) |
Unknown / probe failure | Double (with a TraceWarning logged) |
Identical to the Device Protocol section:
Single-dot Tabletable.Columncolumn only. The registration call rejects multi-dot addresses with the message Multi-segment paths not supported in 10.1.5 , — use 'Tabletable.Columncolumn' with a single dot.
This example browses a QuestDB instance from the Designer UNS tree and registers a column as a UNS tag. Prerequisites: a running QuestDB server with the sensors table from the Installation section above.
QuestDB.| Code Block | ||
|---|---|---|
| ||
localhost;8812;http://localhost:9000;admin;quest;qdb;false |
qdb at level 0 (for example sensors and any other tables you have created).temperature as DOUBLE, plant as SYMBOL, quality as SHORT).temperature column and choose Add to UNS. A tag QuestDBTags/sensors/temperature appears in the UNS Tags list with attribute type Double.@Tag.QuestDBTags/sensors/temperature reads the latest temperature value from the table on each scan cycle.sensors table with the current timestamp.Use this surface when you want FrameworX Historian to write tag samples into QuestDB as a high-throughput append-style time series. This is the natural fit for QuestDB's append-only storage model.
A QuestDB UNS TagProvider row must already exist (previous section). The Historian StorageLocation references it by name.
Historian / Storage Locations / New. The Protocol combo includes QuestDB when the TagProvider row has IsHistorian="true" (the default for 10.1.5). The DataRepository field selects which QuestDB UnsTagProvider to use, with the form TagProvider.<UnsTagProviderName>.
Each FrameworX historian table maps to a QuestDB table. The connector writes rows through ILP/HTTP using the canonical schemaThe QuestDB destination is derived directly from each historized tag's address. An address has the single-dot form table.column; the connector splits it and writes ILP lines as:
| Code Block | ||
|---|---|---|
| ||
<table> <column>=<value>,quality=<q>i timestamp TIMESTAMP (designated timestamp) tag SYMBOL (FrameworX tag address) value DOUBLE (sample value) quality SHORT (FrameworX quality code, 192 = Good) <designated-timestamp> |
So an address of sensors.temperature writes to the QuestDB table sensors, populating the temperature column with the sample value. A second historized tag with address sensors.pressure shares the same QuestDB table - it adds its own pressure column. Multiple historized tags that share a left-side prefix collapse into a single QuestDB table; each contributes one column named after its right-side suffix.
QuestDB's schema-on-write creates the table on the first ILP line if it does not already exist. The resulting columns are:
timestamp by default).DOUBLE for numeric values, the inferred type for strings or booleans).quality column of type LONG (the FrameworX quality code is an unsigned 16-bit value but is written through the ILP integer field path; 192 is Good).Schema-on-write determines partition strategy and column types from the first row written. To choose a specific partition strategy, preQuestDB creates the table on first write through ILP if it does not already exist. Schema-on-write means partition strategy and column types are determined by the first ILP line. Pre-create the table with an explicit CREATE TABLE ... TIMESTAMP(timestamp) PARTITION BY DAY when you need a specific partition strategy in the QuestDB web console before the first historian write.
WriteHistorianDataEx groups the incoming batch by table name, builds ILP lines, and sends a single SendAll round-trip to the IlpEndpoint per drained batch. The driver buffers writes in a background thread (drained every 500 milliseconds) so the platform's poll loop is never blocked on HTTP I/O. Partial-batch failures from QuestDB are surfaced through the per-row bool[] return.
GetSamples runs a range query over the PostgreSQL wire protocol (WHERE timestamp BETWEEN ... AND ...) with ascending sort. The interval and getSamplesMode parameters are ignored in 10.1.5. Raw samples only.
Aggregation (Average, Min, Max, Sum with non-zero interval) is planned for a later release using QuestDB's native SAMPLE BY stage, which performs server-side downsampling efficiently.
This example appends historian samples for a UNS tag to a QuestDB table. Prerequisites: a UNS TagProvider QuestDBTags exists from the previous section and points at the same QuestDB instance, plus a UNS Tag SensorTemp of type Double being updated by a Device, Script, or external source.
TagProvider.QuestDBTags.qdb with the canonical schema documented in the Storage model section above. Subsequent samples are appended via batched ILP writes drained every 500 milliseconds.@Tag.SensorTemp.GetSamples(...) from a Script or Trend control. The connector runs a range query on the timestamp column with ascending sort and returns raw samples. Aggregation is planned for a later release.QuestDB speaks the PostgreSQL wire protocol natively. SQL SELECT queries against historized data, joins, SAMPLE BY downsampling, ASOF JOIN with tolerance, and materialized views all work through the existing PostgreSQL Data Provider with one connection-string flag - no separate QuestDB Dataset Provider is required in 10.1.5.
Server Compatibility Mode=NoTypeLoading - without it the Npgsql client tries to load PostgreSQL system catalog tables that QuestDB does not expose, and the connection fails.Example connection string:
| Code Block | ||
|---|---|---|
| ||
Server=localhost;Port=8812;Database=qdb;Username=admin;Password=quest;Server Compatibility Mode=NoTypeLoading; |
SELECT, joins, WHERE, ORDER BY, LIMIT, OFFSET.SAMPLE BY for server-side downsampling, LATEST ON for latest-row-per-group, ASOF JOIN with tolerance for time-series alignment.LIMIT n OFFSET m.DECLARE CURSOR ... SCROLL) are not available.A QuestDB-branded Dataset Provider entry may be added in a later release if customer feedback warrants a tailored experience. For 10.1.5, the PostgreSQL provider is the supported path.
For an end-to-end Dataset configuration that reads, downsamples (using QuestDB's SAMPLE BY stage), counts, and inserts rows on a QuestDB table (one Dataset DB, three Queries, one Dataset Table, three Script Tasks), see the dedicated example page: Datasets QuestDB Example.
The table below collects per-surface limits that apply to the 10.1.5 release.
Area | Limit | Workaround |
|---|---|---|
Runtime target | ILP write path requires the .NET 10 host. The .NET Framework 4.8 host fails-fast at connect with | Run the solution on a Multiplatform / .NET 10 target. A raw HTTP ILP path that drops the SDK dependency is planned for a later release. |
Device and UNS address format | Single-dot | Schema-level addressing ( |
Device and UNS values | Round-trip as strings on the Device path. | Use the UNS TagProvider for typed registration via the column-type mapping table, or the Dataset Provider for typed query result sets. |
Historian aggregation | Raw samples only. Average, Min, Max, Sum with an interval are not supported. | Request raw samples and aggregate in FX Trend or Script. Server-side |
Historian connection pool | One | Scope time ranges tightly on dashboards. |
UNS browse | Live | A live table-picker combo in the StationEditor is planned for a later release. |
ILP TLS | The TlsEnabled field plumbs as a boolean on the PostgreSQL wire path only. ILP/HTTP TLS is not configurable in 10.1.5. | ILP TLS is planned for a later release. |
Dataset Provider | No QuestDB-branded Dataset Provider entry. Use the PostgreSQL provider with the | Configure as documented in the Dataset Provider section above. |
| Page Tree | ||
|---|---|---|
|