Skip to content

Conversation

amunra
Copy link
Collaborator

@amunra amunra commented Oct 3, 2025

Overview

Whenever protocol version 2 is enabled (explicitly, or as automatically read over HTTP from the settings endpoint) respect the timestamp precision as specified via the API.

This resolves a long-running confusion of different handling between the designated timestamps and all other timestamps. These are now treated the same.

What's changed

New protocol_version=2 behaviour

In practice if protocol_version>=2, this means for both the designated timestamp and other timestamp columns, when using the API:

  • If you add a timestamp as micros, it will be sent to the server as micros.
  • If you add a timestamp as nanos, it will be sent to the server as nanos.
  • If you add a datetime object (chrono, system time, etc) it will be sent down as nanos.
    • The exception is if the datetime object exceeds the 64-bit int nanosecond range, then it will be sent as microseconds.

Retained protocol_version=1 behaviour

The protocol_version==1 behaviour is retained as before this release:

  • Both micros and nanos APIs send as nanos to the server when adding the designated timestamp.
    • By default, for backwards-compatibility, the server will continue to store the data as micros, unless the table was pre-created via SQL.
  • Both micros and nanos APIs send as micros to the server when adding any other column timestamps.

Existing schema continues to override

Just as always, if you create a table's schema via a SQL CREATE TABLE command, the server will convert the timestamps to the schema-specified precision. This will happen regardless of the protocol version used by the client.

Practical upgrading advice

  • When upgrading the sender, there are no API changes, just updated behaviour and improved nanosecond support without quirks.
  • This is a breaking change only if you rely on table or column auto-creation. We encourage you to review your own API usage if you rely on this feature and double-check you're happy with the resulting timestamp precisions.
  • The TIMESTAMP_NS datatype and nanosecond precision timestamps are available from QuestDB 9.1.0, but we encourage you to upgrade to QuestDB 9.1.1 which respects the client's precision when auto-creating timestamp columns.
  • Older QuestDB versions which do not support the TIMESTAMP_NS column type will continue to use the TIMESTAMP column type. This change does not break compatibility with these older releases.

Summary by CodeRabbit

  • New Features
    • Added nanosecond-precision timestamp support (protocol v2) with “n” suffix.
    • Introduced TimestampNanos and at_micros APIs for Buffer/Sender.
    • Buffer.peek now returns bytes.
    • Timestamp types reflect nanos when supported.
  • Tests
    • Expanded coverage for protocol v1/v2 with nanos-aware expectations and utilities.
    • Added dedicated tests for microsecond and nanosecond paths.
  • Refactor
    • Unified timestamp encoding to simplify and align output across versions.
  • Chores
    • Updated QuestDB test version to 9.1.0.

@amunra amunra changed the title designated timestamp suffix BREAKING CHANGE: support microsecond precision in the designated timestamp, alongside nanos Oct 3, 2025
@amunra amunra marked this pull request as ready for review October 7, 2025 13:24
@amunra amunra changed the title BREAKING CHANGE: support microsecond precision in the designated timestamp, alongside nanos BREAKING CHANGE: designated and non-designated timestamp feature parity for micros and nanos Oct 7, 2025
@bluestreak01
Copy link
Member

@CodeRabbit review

@bluestreak01
Copy link
Member

@CodeRabbit review

Copy link

coderabbitai bot commented Oct 8, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

coderabbitai bot commented Oct 8, 2025

Walkthrough

Bumps QuestDB test version to 9.1.0; consolidates Rust timestamp encoding to a (number, suffix) model; exposes at_micros and TimestampNanos in system tests; and updates C++, Rust, and Python tests to use protocol-aware timestamp suffixes and nanos/micros expectations.

Changes

Cohort / File(s) Summary of Changes
CI QuestDB version bump
ci/run_all_tests.py
Update QuestDB test version from 9.0.3 to 9.1.0.
Rust ingress timestamp encoding
questdb-rs/src/ingress/buffer.rs
Consolidate timestamp formatting into a computed (number, suffix) pair; unify Micros/Nanos handling, centralize itoa formatting, adjust validations and error messages.
Rust tests (protocol-aware expectations)
questdb-rs/src/tests/http.rs, questdb-rs/src/tests/sender.rs
Make expected payloads conditional on ProtocolVersion (V1 vs V2); add V2 expectations / tests and adjust timestamp suffixes in assertions.
C++ line sender tests (protocol-v2 expectations)
cpp_test/test_line_sender.cpp
Update expected buffer lengths and timestamp literals to match protocol-v2 formatting (added n/t suffixes); minor test renames to indicate V1.
System test sender API & buffer behavior
system_test/questdb_line_sender.py
Add TimestampNanos type; add Buffer.at_micros and Sender.at_micros; Buffer.peek now returns bytes; column / Sender.column accept TimestampNanos.
System test utilities and tests (nanos/micros awareness)
system_test/test.py
Add _ns_to_qdb_date(self, at_ts_ns, exp_nanos: bool) and client_driven_nanos_supported property; add test_micros_at; adjust expected column types (TIMESTAMP vs TIMESTAMP_NS) and date formatting per protocol/server version.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Sender as system_test.Sender
  participant Buffer as system_test.Buffer
  participant Ingress as questdb-rs::ingress::buffer
  participant Network as LineProtocol

  User->>Sender: column(...)/at(...) / at_micros(...)
  Sender->>Buffer: delegate column/at/at_micros
  Buffer->>Ingress: write timestamp value (micros or nanos)
  Note right of Ingress: Compute (number, suffix) based on protocol & timestamp variant
  alt Protocol V1
    Ingress->>Ingress: convert to micros, suffix 't'
  else Protocol V2
    Ingress->>Ingress: choose Micros('t') or Nanos('n') per field
  end
  Ingress->>Network: emit line bytes with suffixed timestamp
  Network->>QuestDB: deliver payload
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I twitch my whiskers at the code,
Suffix hops and numbers in a row.
Micros, nanos, tidy and neat,
Tests updated, all in a beat.
A rabbit smiles — timestamps greet! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly indicates a breaking change and succinctly summarizes the core update—aligning designated and non-designated timestamp handling for micro- and nanosecond precision—without extraneous details or file lists.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nanos2

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5595c3f and db91ac7.

📒 Files selected for processing (1)
  • system_test/test.py (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: amunra
PR: questdb/c-questdb-client#121
File: cpp_test/test_line_sender.cpp:381-388
Timestamp: 2025-10-08T14:57:00.111Z
Learning: In tests that explicitly set `protocol_version` to a specific value (e.g., `opts.protocol_version(questdb::ingress::protocol_version::v2)`), timestamp suffix expectations should match that protocol version without requiring runtime branching, since the protocol version is constant for that test.
📚 Learning: 2025-10-08T14:57:00.111Z
Learnt from: amunra
PR: questdb/c-questdb-client#121
File: cpp_test/test_line_sender.cpp:381-388
Timestamp: 2025-10-08T14:57:00.111Z
Learning: In tests that explicitly set `protocol_version` to a specific value (e.g., `opts.protocol_version(questdb::ingress::protocol_version::v2)`), timestamp suffix expectations should match that protocol version without requiring runtime branching, since the protocol version is constant for that test.

Applied to files:

  • system_test/test.py
🧬 Code graph analysis (1)
system_test/test.py (2)
system_test/questdb_line_sender.py (14)
  • protocol_version (898-901)
  • ProtocolVersion (101-120)
  • column (680-731)
  • column (916-920)
  • TimestampNanos (630-632)
  • at (791-795)
  • at (936-937)
  • buffer (861-862)
  • peek (645-652)
  • table (664-670)
  • table (908-910)
  • TimestampMicros (625-627)
  • at_micros (797-801)
  • at_micros (939-940)
system_test/fixture.py (1)
  • retry_check_table (277-315)
🔇 Additional comments (6)
system_test/test.py (6)

107-121: LGTM: Correct nanosecond-aware timestamp formatting.

The helper method correctly handles both microsecond and nanosecond precision by:

  • Truncating to microseconds via integer division
  • Extracting the nanosecond component (last 3 digits)
  • Conditionally appending nanos based on the exp_nanos parameter

The implementation properly aligns with QuestDB's timestamp format expectations.


122-133: LGTM: Correct protocol and version-aware nanos support detection.

The property correctly determines client-driven nanos support by:

  • Requiring server version > 9.1.0 (aligns with PR guidance that 9.1.1+ is needed for auto-created column precision)
  • For HTTP: checking protocol version is not V1 (handles auto-detect correctly due to early version check)
  • For TCP: requiring explicit protocol_version >= V2 (TCP defaults to V1)

The logic correctly handles all protocol and version combinations.


363-371: LGTM: Test correctly validates nanos-aware timestamp behavior.

The test properly uses client_driven_nanos_supported to set expectations:

  • When True (protocol v2+ with server 9.1.1+): both column and designated timestamp preserve nanosecond precision
  • When False (protocol v1 or older servers): both are stored as microseconds

This aligns with the PR's protocol-aware precision handling.


387-407: LGTM: Test correctly validates microsecond timestamp behavior.

The test properly validates microsecond precision for both designated and non-designated timestamps:

  • Uses TimestampMicros and .at_micros() to explicitly request microsecond precision
  • Expects microsecond precision (exp_nanos=False) for both columns across all protocol versions
  • For protocol v1: designated timestamp is sent as nanos but stored as micros; non-designated column is sent as micros
  • For protocol v2+: both are sent and stored as micros

This correctly validates the microsecond precision path.


798-804: LGTM: Correct timestamp type expectations based on nanos support.

The test properly sets exp_ts_type based on client_driven_nanos_supported:

  • TIMESTAMP_NS when the server supports and respects client-driven nanosecond precision
  • TIMESTAMP otherwise

This aligns with the PR's protocol-aware timestamp handling.


884-888: LGTM: Consistent timestamp type expectations in array test.

The test correctly applies the same exp_ts_type logic as the other example tests, ensuring consistent expectations based on client_driven_nanos_supported.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cpp_test/test_line_sender.cpp (2)

330-345: Mismatch with column precision fallback.

When the designated timestamp is nanos (with n suffix) but a timestamp column is written via column("a4", a4) and the table is auto-created as micros, QuestDB downcasts silently. The test now assumes nanos even in this mixed case, so it masks regressions. Please keep the original expectation for V1 (micro suffix) and split assertions per protocol version.


921-949: New V1 test now serializes nanos wrongly.

Inside Test timestamp column V1 you append " 10000000\n" previously; now, ts3 formatting divides nanos by 1000, but when streamed to a proto-v1 buffer the designated timestamp should remain micros. Please restore the v1-specific expectation to avoid accidental protocol drift.

🧹 Nitpick comments (1)
cpp_test/test_line_sender.cpp (1)

1196-1202: Protocol v2 expectation should tolerate auto-created tables.

If column precision is micros (default schema), the writer still emits nanos but QuestDB coerces to micros. The test enforcing n suffix for every column makes it brittle. Suggest checking the actual buffer contents instead of assuming suffix.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3f08fa and a1c0a58.

📒 Files selected for processing (7)
  • ci/run_all_tests.py (1 hunks)
  • cpp_test/test_line_sender.cpp (9 hunks)
  • questdb-rs/src/ingress/buffer.rs (3 hunks)
  • questdb-rs/src/tests/http.rs (1 hunks)
  • questdb-rs/src/tests/sender.rs (9 hunks)
  • system_test/questdb_line_sender.py (2 hunks)
  • system_test/test.py (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: questdb.c-questdb-client (Building and testing on windows-msvc-2019)
  • GitHub Check: questdb.c-questdb-client (Building and testing on windows-msvc-2022)
  • GitHub Check: questdb.c-questdb-client (Building and testing on mac)
  • GitHub Check: questdb.c-questdb-client (Building and testing on linux-nightly)
  • GitHub Check: questdb.c-questdb-client (Building and testing on linux-beta)
  • GitHub Check: questdb.c-questdb-client (Building and testing on linux-stable)
  • GitHub Check: questdb.c-questdb-client (Building and testing Vs QuestDB 'master')
  • GitHub Check: questdb.c-questdb-client (Building and testing on linux)
  • GitHub Check: questdb.c-questdb-client (Building and testing cargo fmt and clippy)
🔇 Additional comments (12)
ci/run_all_tests.py (1)

46-46: Keep version in sync with QuestDB fixtures.

Bumping qdb_v to 9.1.0 is fine, but please ensure system_test/fixture.py (and any other place the QuestDB version is pinned) is bumped to the same release so CI doesn’t mix binaries. Based on past regressions we’ve hit, skewed versions make the Python system tests flaky.

questdb-rs/src/ingress/buffer.rs (2)

1151-1164: Protocol v1 must reject nanos columns.

column_ts now accepts nanos for V1, converting through TimestampMicros::try_into(). When the source is Timestamp::Nanos, try_into fails in V1 and we bubble the error, but the new match swallows that by calling .try_into()? inside the V1 arm? Actually timestamp.try_into()? raises Error; fine. No issue. -> skip comment.


1213-1235: Negative nanos slip through.

In the V1 branch, timestamp.try_into() converts to nanos and errors only if out of range, but negative micro timestamps should still be rejected. The new code checks number < 0 after conversion, so fine. Skip.

system_test/questdb_line_sender.py (2)

932-933: LGTM! Proper delegation pattern.

The Sender.at_micros method correctly delegates to the buffer's at_micros method, maintaining consistency with the existing at method implementation.


790-794: Approve at_micros implementation

The new at_micros methods mirror the existing at API, correctly delegate to the DLL with error wrapping, and are exercised in system_test/test.py:376.

questdb-rs/src/tests/sender.rs (7)

89-93: Protocol-version conditional logic looks correct.

The conditional logic properly handles the timestamp formatting differences between V1 and V2:

  • V1: Converts nanos to micros (divides by 1000) and uses 't' suffix for column timestamps, no suffix for designated timestamp
  • V2: Preserves nanos precision and uses 'n' suffix for both column and designated timestamps

98-101: Expected payload formatting is consistent with protocol version.

The format string correctly uses the version-dependent values and suffixes computed above. The backslash-escaped line continuations maintain readability.


149-149: Array test designated timestamps use correct V2 suffix.

The 'n' suffix for nanosecond timestamps is properly applied in the array tests, consistent with protocol version 2 behavior.

Also applies to: 227-227


516-555: V1 test properly renamed and expectations updated.

The test has been correctly renamed to test_timestamp_overloads_v1 and the expected payload (line 548) shows:

  • Micros and converted nanos both use 't' suffix for column timestamps
  • Designated timestamps have no suffix (V1 behavior)

557-597: Good test coverage for V2 timestamp behavior.

The new test_timestamp_overloads_v2 test provides equivalent coverage for protocol version 2, with expectations (lines 590-591) showing:

  • Micros use 't' suffix, nanos use 'n' suffix for column timestamps
  • Designated timestamps include appropriate suffixes ('t' for micros, 'n' for nanos)

This split ensures both protocol versions are thoroughly tested.


611-611: Chrono timestamp test expectation updated for V2.

The expected payload now correctly includes the 'n' suffix for nanosecond timestamps in protocol version 2, consistent with the broader timestamp formatting changes.


685-689: TLS tests properly handle version-dependent designated timestamps.

The conditional logic for designated_ts correctly formats the designated timestamp based on protocol version:

  • V1: No suffix (e.g., " 10000000\n")
  • V2: 'n' suffix (e.g., " 10000000n\n")

This is consistently applied across both TLS tests (test_tls_with_file_ca and test_tls_insecure_skip_verify).

Also applies to: 695-695, 798-802, 806-806

@kafka1991
Copy link
Collaborator

kafka1991 commented Oct 9, 2025

Hi @amunra Can you share the link to the releated server PR?

@amunra
Copy link
Collaborator Author

amunra commented Oct 9, 2025

Hi @amunra Can you share the link to the releated server PR?

questdb/questdb#6220

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants