Skip to content

docs(rfc-0005): spec the effective-timestamp fallback (time==0 → observed) as a derived column - #178

Merged
jensholdgaard merged 4 commits into
mainfrom
docs/rfc-0005-effective-timestamp
Jun 11, 2026
Merged

docs(rfc-0005): spec the effective-timestamp fallback (time==0 → observed) as a derived column#178
jensholdgaard merged 4 commits into
mainfrom
docs/rfc-0005-effective-timestamp

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jun 11, 2026

Copy link
Copy Markdown
Owner

What

DOC-ONLY RFC amendment (no status changes, no code): specs the effective-timestamp fallback — records with time_unix_nano == 0 fall back to observed_time_unix_nano for partitioning and time-window queries, per the OTLP logs data model. Maintainer-decided 2026-06-11, option 1: ingest-side, derived — never overwriting the wire value.

The measured blocker

~15% of records in real OTel-Demo corpora (v5: 205,155 records; v6: 202,484) carry timeUnixNano absent/0, and 100% of those carry observedTimeUnixNano (verified by sampling). Today those records are unaddressable by time — the DSL window filters time_unix_nano, so they sit outside every real query window — and the B1 bench's zero-ts guard correctly refuses such corpora. B1 is the last unmeasured thesis gate.

The OTLP-spec citation

Quoted verbatim in the RFC 0005 §3.2 amendment, from the OTLP logs data model (Field: Timestamp / Field: ObservedTimestamp, https://opentelemetry.io/docs/specs/otel/logs/data-model/):

Time when the event occurred measured by the origin clock, i.e. the time at the source. This field is optional, it may be missing if the source timestamp is unknown.

Time when the event was observed by the collection system. […] This field SHOULD be set once the event is observed by OpenTelemetry.

For converting OpenTelemetry log data to formats that support only one timestamp or when receiving OpenTelemetry log data by recipients that support only one timestamp internally the following logic is recommended:

  • Use Timestamp if it is present, otherwise use ObservedTimestamp.

The design

  • Derivation (writer-side, derived): effective_time_unix_nano := time_unix_nano if time_unix_nano != 0 else observed_time_unix_nano.unwrap_or(0), computed by the Parquet writer from the two existing record fields — the same rule RFC 0005 §3.4 (and PartitionKey::derive) already runs for partitioning, now stored. MinedRecord / OtlpLogRecord are unchanged; the wire time_unix_nano is stored verbatim including 0RFC 0001 scenario RFC0001.10 (verbatim preservation) is explicitly intact.
  • Storage: new OPTIONAL column per §3.8 rule 1 (additive). Redundancy trade documented: ≈8 B/row pre-encoding, almost always equals time_unix_nano so DELTA_BINARY_PACKED + ZSTD collapse it; a real column (with min/max stats, §3.6) is what keeps the window predicate row-group-pruneable — a query-time coalesce() would defeat stats pruning.
  • Partitioning: §3.4's existing fallback is named as this rule; partition tuple and stored column never disagree. Only genuinely timeless records (neither timestamp) remain under the 1970 epoch partition.
  • Query semantics: range(...) filters effective_time_unix_nano (RFC 0002 §6.2 amended); the bare ts field stays on the verbatim time_unix_nano.
  • Absent-column read rule (the migration story): files written before this amendment lack the column; the documented default (§3.9 rule 2) is effective := time_unix_nano — exactly the pre-amendment behaviour, so historical files keep answering queries identically. The querier's absent-OPTIONAL ⇒ predicate-false convention (RFC0007.4) explicitly does not apply to the window filter — that would silently hide all pre-amendment data.
  • Acceptance: new scenario RFC0005.13 (zero-ts + observed → partitioned/windowed by observed, wire 0 preserved; old file without the column → behaves as time_unix_nano) + §6 testing-strategy entry.
  • Bench note: the B1 zero-ts guard then keys off the effective span — a code follow-up, not this PR.

Files touched (docs/rfcs only)

  • docs/rfcs/0005-parquet-storage.md — §3.2 column + dated amendment (with the spec quotes), §3.4, §3.6, §3.9, §5 RFC0005.13, §6.
  • docs/rfcs/0002-query-dsl.md — §6.2: the ts table row previously said time_unix_nano is "what range(...) filters"; that text fought the design head-on, so it carries a dated amendment note (the only RFC touched beyond the planned three — without it the doc set would self-contradict on what the window filters).
  • docs/rfcs/0001-template-miner.md — §6.1 cross-ref note: record shape unchanged, RFC0001.10 intact.
  • docs/rfcs/0003-otlp-receiver.md — §6.6 cross-ref note: receiver contract unchanged (wire-0None rule for observed stands; nothing materialised on OtlpLogRecord).

Invariants / hazards (CLAUDE.md §3.5, §4 H5/H6)

Schema change goes through the RFC gate as required: additive OPTIONAL column (§3.8 rule 1 — the preferred §3.5 path), explicit migration story for historical files (the §3.9 absent-column default), readers keep handling absent columns without error. No status change on any RFC.

Verification

  • mdbook build clean (benign mermaid version warning only); amended sections re-read for internal consistency.
  • git status shows only docs/rfcs/*.md — no code, no schema bytes.
  • cargo fmt/clippy/test not run: doc-only change, no Rust touched.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Clarified timestamp handling semantics across system components to ensure consistency in data storage and query operations.
    • Enhanced specifications for time-based filtering to improve query accuracy and predictability.
    • Updated backward compatibility guidance for existing data formats to maintain seamless transitions.

…rved) as a derived column

Records whose time_unix_nano == 0 (~15% of real OTel-Demo corpora —
v5: 205,155 records, v6: 202,484 — 100% of which carry
observed_time_unix_nano) are unaddressable by time today: the DSL
window filters time_unix_nano, so they sit outside every real query
window, and the B1 bench's zero-ts guard correctly refuses such
corpora. The OTLP logs data model recommends "Use Timestamp if it is
present, otherwise use ObservedTimestamp" — this amendment adopts that
as an ingest-side derived value (maintainer decision 2026-06-11,
option 1), never overwriting the wire value.

- RFC 0005 §3.2: new OPTIONAL effective_time_unix_nano column
  (§3.8 rule 1 additive), writer-derived as
  `time_unix_nano if != 0 else observed_time_unix_nano.unwrap_or(0)`,
  with the OTLP Timestamp/ObservedTimestamp field definitions quoted.
- RFC 0005 §3.4: the existing partition time-fallback is named as
  this rule; partition tuple and stored column never disagree.
- RFC 0005 §3.6: encoding row (no dict, page index, delta-encodes —
  min/max stats make the B1 window predicate pruneable).
- RFC 0005 §3.9: old-file read rule — absent column defaults to the
  row's time_unix_nano (not None), so pre-amendment files answer
  time-window queries exactly as before; the absent-OPTIONAL ⇒
  predicate-false convention explicitly does not apply here.
- RFC 0005 §5/§6: scenario RFC0005.13 + testing-strategy entry.
- RFC 0002 §6.2: range(...) now filters the effective column; the
  bare `ts` field stays on the verbatim time_unix_nano.
- RFC 0001 §6.1 / RFC 0003 §6.6: cross-ref notes — record/receiver
  shapes unchanged, RFC0001.10 verbatim preservation explicitly
  intact.

No RFC status changes. B1 guard keying off the effective span is a
code follow-up, not this PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jensholdgaard
jensholdgaard requested a review from Copilot June 11, 2026 19:24
@jensholdgaard

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jensholdgaard, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 6 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f8692ec5-dc02-418b-bb46-3ffaa3c8e165

📥 Commits

Reviewing files that changed from the base of the PR and between 9b08c84 and a202474.

📒 Files selected for processing (3)
  • docs/rfcs/0002-query-dsl.md
  • docs/rfcs/0003-otlp-receiver.md
  • docs/rfcs/0005-parquet-storage.md
📝 Walkthrough

Walkthrough

This PR amends four RFCs to define and integrate a derived effective_time_unix_nano timestamp column. The column implements fallback logic (prefer time_unix_nano when non-zero, else observed_time_unix_nano) for time-window filtering and partition key derivation while preserving the wire time_unix_nano value verbatim, including 0 for "unknown".

Changes

Effective timestamp derivation across RFC chain

Layer / File(s) Summary
Derivation contract and upstream sources
docs/rfcs/0001-template-miner.md, docs/rfcs/0003-otlp-receiver.md, docs/rfcs/0005-parquet-storage.md
RFC 0001 and 0003 are amended to clarify that the miner record and OTLP receiver wire fields remain unchanged; RFC 0005 adds effective_time_unix_nano as an OPTIONAL derived column with fallback logic and specifies it is additive and never overwrites either wire field.
Query-time filtering and reader contract defaults
docs/rfcs/0002-query-dsl.md, docs/rfcs/0005-parquet-storage.md
RFC 0002 updates the canonical-fields table to specify that range(...) filtering targets the derived effective_time_unix_nano with legacy fallback for older files; RFC 0005 amends the reader contract to default missing effective_time_unix_nano to the row's time_unix_nano value.
Partition layout and column encoding
docs/rfcs/0005-parquet-storage.md
RFC 0005 clarifies that time-bucket partition keys (year/month/day/hour) derive from effective_time_unix_nano using the same fallback logic; encoding policy is added to match time_unix_nano with timestamp encoding and page min/max pruning for predicate pushdown.
Acceptance criteria and integration testing
docs/rfcs/0005-parquet-storage.md
RFC 0005 adds acceptance scenario RFC0005.13 and an integration test bullet covering writer derivation output, query-time filtering, wire timestamp preservation, and pre-amendment compatibility for files lacking the new column.

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • jensholdgaard/ourios#143: Both PRs modify RFC 0002 Query DSL to clarify timestamp semantics for time-range filtering, including canonical field handling and effective timestamp fallback behavior.
  • jensholdgaard/ourios#83: Both PRs define timestamp handling across RFCs; this PR updates RFC 0005 Parquet storage contract for effective_time_unix_nano derivation and filtering, while the retrieved PR specifies querier lowering that must target and push down those predicates correctly.

Poem

🐰 A timestamp's tale takes shape today,
Fallback logic leads the way,
Effective when the wire won't tell,
RFCs align, the readers spell.
Through partition paths and queries bright,
The derived nano shines so right! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a derived effective-timestamp column that falls back from time_unix_nano to observed_time_unix_nano when time is zero.
Description check ✅ Passed The description is comprehensive and well-structured. It covers the what, why, design rationale, affected files, and verification steps, though it does not fully follow the template's checklist items.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/rfc-0005-effective-timestamp

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.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR amends the documentation RFCs to specify an effective timestamp used for Parquet partitioning and time-window queries: when time_unix_nano == 0, fall back to observed_time_unix_nano, and persist that derived value as a new OPTIONAL Parquet column (effective_time_unix_nano) without overwriting the wire timestamp.

Changes:

  • RFC 0005: Specify the new derived Parquet column effective_time_unix_nano, align partitioning and query-window semantics to it, and document the old-file (absent column) default behavior.
  • RFC 0002: Update the Query DSL documentation so range(...) is defined to filter effective_time_unix_nano (with the documented pre-amendment fallback).
  • RFCs 0001 and 0003: Add cross-reference amendment notes clarifying record/receiver shapes remain unchanged and the derivation happens in the Parquet writer.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
docs/rfcs/0005-parquet-storage.md Adds effective_time_unix_nano to the Parquet schema and defines partitioning/window semantics + migration story.
docs/rfcs/0002-query-dsl.md Updates range(...) semantics to filter the effective timestamp rather than time_unix_nano.
docs/rfcs/0001-template-miner.md Notes the effective timestamp is writer-derived and does not change mined record shape.
docs/rfcs/0003-otlp-receiver.md Notes the receiver contract is unchanged; effective timestamp is derived downstream.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/rfcs/0005-parquet-storage.md Outdated
Comment thread docs/rfcs/0005-parquet-storage.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
docs/rfcs/0005-parquet-storage.md (1)

748-786: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep legacy files on the pre-amendment partition rule.

§3.9 says files missing effective_time_unix_nano should behave as time_unix_nano, but this reader-validation paragraph still applies the new observed-timestamp fallback to every file. That can make older rows with time_unix_nano = 0 and a non-zero observed timestamp fail Reader::open_partition even though they were written before this amendment.

🔧 Suggested wording fix
- The derivation algorithm is identical to the writer's in §3.4: prefer
- `time_unix_nano` if non-zero, else fall back to
- `observed_time_unix_nano` if present and non-zero, else the
- 1970-01-01T00 epoch.
+ For files that include `effective_time_unix_nano`, validate the
+ partition tuple against the §3.4 effective-timestamp rule.
+ For legacy files without that column, validate against the
+ pre-amendment `time_unix_nano` path only.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/rfcs/0005-parquet-storage.md` around lines 748 - 786, The
partition-validation currently applies the new observed-timestamp fallback to
all files; change Reader::open_partition so it detects per-file whether the
amendment column effective_time_unix_nano exists and, for files that lack that
column (pre-amendment), compute the derived time for validation as effective :=
time_unix_nano only (do not fall back to observed_time_unix_nano), while
preserving the existing observed-time fallback behavior for files that do have
effective_time_unix_nano; keep Reader::open_file's current behavior of skipping
partition validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/rfcs/0002-query-dsl.md`:
- Around line 369-385: Update the RFC amendment text to precisely define
effective_time_unix_nano as "time_unix_nano if time_unix_nano != 0 else
observed_time_unix_nano.unwrap_or(0)" (make the unwrap/zero handling explicit)
and explicitly document the semantics of range(from, to) as half-open [from, to)
(or state the chosen inclusive/exclusive policy); reference the existing symbols
range(...), effective_time_unix_nano, ts, time_unix_nano, and
observed_time_unix_nano in the amendment paragraph so the contract is
unambiguous.

---

Outside diff comments:
In `@docs/rfcs/0005-parquet-storage.md`:
- Around line 748-786: The partition-validation currently applies the new
observed-timestamp fallback to all files; change Reader::open_partition so it
detects per-file whether the amendment column effective_time_unix_nano exists
and, for files that lack that column (pre-amendment), compute the derived time
for validation as effective := time_unix_nano only (do not fall back to
observed_time_unix_nano), while preserving the existing observed-time fallback
behavior for files that do have effective_time_unix_nano; keep
Reader::open_file's current behavior of skipping partition validation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f394ce1-ae94-4c2b-b89d-ef3ca7c91037

📥 Commits

Reviewing files that changed from the base of the PR and between e29231b and 9b08c84.

📒 Files selected for processing (4)
  • docs/rfcs/0001-template-miner.md
  • docs/rfcs/0002-query-dsl.md
  • docs/rfcs/0003-otlp-receiver.md
  • docs/rfcs/0005-parquet-storage.md

Comment thread docs/rfcs/0002-query-dsl.md Outdated
…mn precision

A plain coalesce never falls back (time_unix_nano is REQUIRED with a 0
sentinel) — the example is now the CASE form. The §3.6 time row points
at effective_time_unix_nano as the primary window column. RFC 0002 pins
the exact derivation (unwrap_or(0)) and half-open [from,to) bounds,
aligning with the querier + RFC 0010.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread docs/rfcs/0002-query-dsl.md
Comment thread docs/rfcs/0002-query-dsl.md Outdated
The window SHALL compile against effective_time_unix_nano (the
implementing slice follows; today the querier filters time_unix_nano);
the half-open shape is the already-implemented part.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

Comment thread docs/rfcs/0002-query-dsl.md Outdated
Comment thread docs/rfcs/0005-parquet-storage.md
Comment thread docs/rfcs/0005-parquet-storage.md Outdated
Comment thread docs/rfcs/0003-otlp-receiver.md Outdated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jensholdgaard
jensholdgaard merged commit 407c12a into main Jun 11, 2026
9 of 10 checks passed
jensholdgaard added a commit that referenced this pull request Jun 11, 2026
…05.13) (#179)

Implements the #178 amendment. effective_time_unix_nano (OPTIONAL, stats-bearing) derived by the shared effective_time_unix_nano() fn (also PartitionKey::derive + the bench adapter — the never-disagree rule is structural). Querier time windows filter the new column with the §3.9 carve-out: absent column → time_unix_nano as before; present → (eff in-window) OR (eff IS NULL AND ts in-window), the IS NULL arm identifying exactly the DataFusion-NULL-filled pre-amendment rows; OR-shape stays inside the pruning grammar (two-row-group pruning test). Bench B1 eligibility keys off the effective span — observed-only corpora (v5/v6, ~15%) become measurable. Old files keep answering identically (CLAUDE.md §3.5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants