Skip to content

tsdb/chunkenc: add XOR2 chunk encoding and docs#18238

Closed
roidelapluie wants to merge 4 commits into
prometheus:mainfrom
roidelapluie:roidelapluie/xor2
Closed

tsdb/chunkenc: add XOR2 chunk encoding and docs#18238
roidelapluie wants to merge 4 commits into
prometheus:mainfrom
roidelapluie:roidelapluie/xor2

Conversation

@roidelapluie

@roidelapluie roidelapluie commented Mar 5, 2026

Copy link
Copy Markdown
Member

Which issue(s) does the PR fix:

Does this PR introduce a user-facing change?

[ENHANCEMENT] TSDB: New chunks encoding. 

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie
roidelapluie force-pushed the roidelapluie/xor2 branch 2 times, most recently from 9fd90a2 to ce2b180 Compare March 5, 2026 11:20
Comment thread tsdb/chunkenc/xor2.go
a.writeVDelta(v)
default:
tDelta = uint64(t - a.t)
dod := int64(tDelta - a.tDelta)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Probably tDod would be bit more clear

Suggested change
dod := int64(tDelta - a.tDelta)
tDod := int64(tDelta - a.tDelta)

@roidelapluie

Copy link
Copy Markdown
Member Author
release PR relative gain
Block ULID 01KJZ39JEKRTRK3B39KQ7EWPPS 01KJZ37KZTKFX3N0ZCPG62HABS
Samples 237,363,945 237,404,388
Block size 757.0 MB 730.6 MB 3.49%
Chunks size 458.6 MB 432.1 MB 5.78%
bytes/sample 2.03 1.91 5.91%

@roidelapluie

Copy link
Copy Markdown
Member Author

/prombench restart main

@prombot

prombot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

⏱️ Welcome (again) to Prometheus Benchmarking Tool. ⏱️

Compared versions: PR-18238 and main

After successful deployment (check status here), the benchmarking results can be viewed at:

Available Commands:

  • To restart benchmark: /prombench restart main
  • To stop benchmark: /prombench cancel
  • To print help: /prombench help

@roidelapluie

Copy link
Copy Markdown
Member Author

/prombench cancel

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

Copy link
Copy Markdown
Member Author
Block (by time) PR chunks PR B/sample Release chunks Release B/sample gain
01KK03S... 880.8 MB 1.94 923.4 MB 2.04 4.80%
01KK0HC... 566.4 MB 1.89 594.7 MB 1.99 4.67%
01KK0R8... 602.2 MB 2.01 623.0 MB 2.09 3.48%
01KK0RD... 1700.0 MB 1.86 1800.0 MB 1.97 5.61%
01KK0Z4... 585.6 MB 1.96 626.6 MB 2.10 6.69%
01KK160... 579.0 MB 1.93 626.2 MB 2.09 7.60%
TOTAL 4914.0 MB 1.92 5193.9 MB 2.03 5.39%

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

Copy link
Copy Markdown
Member Author

I will try to reduce a bit more the variance in prombench to asses the CPU difference.

While 5% is not a 15% gain, I have seen workloads where we reach the 15%. On the 40 workloads I have found, none had regressions in disk size with this algorithm, so I am fairly confident it will benefit every user.

@roidelapluie

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

krajorama added a commit that referenced this pull request Mar 6, 2026
XOR2 is based on #18238
With additional ST support.

Signed-off-by: György Krajcsovits <[email protected]>
@prombot

prombot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

⏱️ Welcome (again) to Prometheus Benchmarking Tool. ⏱️

Compared versions: PR-18238 and main

After successful deployment (check status here), the benchmarking results can be viewed at:

Available Commands:

  • To restart benchmark: /prombench restart main
  • To stop benchmark: /prombench cancel
  • To print help: /prombench help

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie
roidelapluie force-pushed the roidelapluie/xor2 branch 3 times, most recently from 96987f6 to c8ee650 Compare March 13, 2026 16:38
@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

XOR2Chunk uses a sync.Pool (xor2ChunkPool) so that mmap'd chunks can
donate their grown backing slice to the next head chunk.  The contract
is that ReleaseXOR2Chunk() is called only after the chunk is fully
written to disk and no longer referenced.

ChunkDiskMapper violated this contract in two places:

- Async queue path: WriteChunk() returns before the background writer
  has consumed the chunk, so ReleaseXOR2Chunk() could recycle the chunk
  while the queue still held its pointer.  NewXOR2Chunk() would then
  reuse the pointer and overwrite the bytes the writer was about to
  flush, producing corrupt on-disk data and an eventual panic in
  NumSamples() when the empty/overwritten stream was later read back.

- Synchronous path (default): writeChunk() stores the chunk pointer
  directly in chunkBuffer.  ReleaseXOR2Chunk() followed by
  NewXOR2Chunk() can then overwrite the buffered chunk's bytes before
  a reader retrieves it via Chunk().

Fix both by cloning the XOR2 chunk bytes into an independent copy
(via cloneRetainedXOR2Chunk) before the chunk is handed to either the
queue or the buffer.  ReleaseXOR2Chunk() can then safely recycle the
original at any point after WriteChunk() returns.

Add regression tests for both paths that block or sequence writes,
release and reuse the original chunk, then assert the stored data
matches the original samples.

Signed-off-by: Julien Pivotto <[email protected]>
@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

… path

Remove the sync.Pool introduced in the previous commit along with all
clone-on-write machinery it required (cloneRetainedXOR2Chunk,
ReleaseXOR2Chunk, xor2MaxPooledCapacity). The pool added complexity and
a corruption risk that outweighs the allocation savings.

In its place, add two targeted optimisations:
- Cache the sample count in xor2Appender.num so each Append call no
  longer reads the two header bytes via binary.BigEndian.Uint16.
- Add a 3-bit fast path in xor2Iterator.readValue that decodes the full
  control prefix in one integer operation when the bit buffer holds at
  least 3 bits, avoiding per-bit branching on the common hot path.

Signed-off-by: Julien Pivotto <[email protected]>
@roidelapluie

This comment was marked as outdated.

@prombot

This comment was marked as outdated.

…te GC pressure

XOR2Chunk previously embedded an xor2Appender struct which contains a
*bstream pointer field. After Appender() is called, app.b points back to
&c.b within the same allocation, creating an intra-object self-pointer.
The GC must call runtime.findObject for every live XOR2Chunk to resolve
that pointer, which is O(log N) in heap size. With millions of active
head chunks this dominates GC scan time.

Production CPU profiles confirm the overhead: runtime.findObject at
5.79% flat and runtime.scanobject at 9.71% cumulative in XOR2 vs
essentially zero in XOR.

Allocate xor2Appender on the heap in Appender() instead, matching the
XOR approach. Appenders are short-lived (alive only during ingestion)
while chunks live for minutes, so the allocation cost is negligible.

Signed-off-by: Julien Pivotto <[email protected]>
@roidelapluie

This comment was marked as outdated.

@prombot

prombot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

⏱️ Welcome (again) to Prometheus Benchmarking Tool. ⏱️

Compared versions: PR-18238 and main

Custom benchmark version: roidelapluie/xor2-encoding branch

After successful deployment (check status here), the benchmarking results can be viewed at:

Available Commands:

  • To restart benchmark: /prombench restart main --bench.version=roidelapluie/xor2-encoding
  • To stop benchmark: /prombench cancel
  • To print help: /prombench help

bwplotka added a commit that referenced this pull request Mar 19, 2026
…ncoding` (#18062)

* feat(tsdb/chunkenc): add float chunk format with start timestamp support


Signed-off-by: György Krajcsovits <[email protected]>

* optimize code path and layout

Signed-off-by: György Krajcsovits <[email protected]>

* make new format usable in head

Signed-off-by: György Krajcsovits <[email protected]>

* fix issue with seeking to last sample again

Signed-off-by: György Krajcsovits <[email protected]>

* fix iterator benchmark for chunks not supporting ST

Signed-off-by: György Krajcsovits <[email protected]>

* reduce footprint of the xoroptst chunk iterator object

It was 80 bytes with a lot of padding compared to the 56 bytes of the
original xor chunk iterator. Made it 64 bytes, tightly packed.

Signed-off-by: György Krajcsovits <[email protected]>

* Fix benchmark expectations on ST in interator

Signed-off-by: György Krajcsovits <[email protected]>

* add inclusive delta test case

Signed-off-by: György Krajcsovits <[email protected]>

* make testcases independent of order

Signed-off-by: György Krajcsovits <[email protected]>

* drop unused code

Signed-off-by: György Krajcsovits <[email protected]>

* Drop commented out line

Signed-off-by: György Krajcsovits <[email protected]>

* documentation

Signed-off-by: György Krajcsovits <[email protected]>

* Small simplification in the doc

Signed-off-by: György Krajcsovits <[email protected]>

* Add delta st inclusive test case for random vt

Signed-off-by: György Krajcsovits <[email protected]>

* Switch to delta of difference of st to prev t

from delta of delta of st.

Signed-off-by: György Krajcsovits <[email protected]>

* Write ST after T and V so we can write a single bit on the second sample

Signed-off-by: György Krajcsovits <[email protected]>

* verify chunk sample len function

Signed-off-by: György Krajcsovits <[email protected]>

* Reduce size of first st stored a little

Signed-off-by: György Krajcsovits <[email protected]>

* test the case where st equals the t

Signed-off-by: György Krajcsovits <[email protected]>

* add st equal t to bechmarks

Signed-off-by: György Krajcsovits <[email protected]>

* test(chunkenc): test that appender can contonue chunks

Test that initializing a chunk appender from an existing chunk
works correctly.

Signed-off-by: György Krajcsovits <[email protected]>

* fix(chunkenc): bug in initializing appender on existing chunk

Signed-off-by: György Krajcsovits <[email protected]>

* Add cases with jitter in the start time as well


Signed-off-by: György Krajcsovits <[email protected]>

* tsdb: ST-in-WAL: Counter implementation and benchmarks (#17671)

Initial implementation of #17790.
Only implements ST-per-sample for Counters. Tests and benchmarks updated.

Note: This increases the size of the RefSample object for all users, whether st-per-sample is turned on or not.

Signed-off-by: Owen Williams <[email protected]>

* refactor: sed enableStStorage/enableSTStorage

Signed-off-by: bwplotka <[email protected]>

* feat[scrape]: add ST parsing support to scrape AppenderV2 flow (#18103)

Signed-off-by: bwplotka <[email protected]>

* feat(tsdb): change head opt EnableSTStorage to atomic (#18107)

In downstream projects this needs to be set dynamically per tenant.

Signed-off-by: György Krajcsovits <[email protected]>

* Merge pull request #18108 from prometheus/bwplotka/fix

scrape: add tests for ST appending; add warnings for ST feature flag users around _created drop

* refact(tsdb): trivial rename (#18109)

Signed-off-by: György Krajcsovits <[email protected]>

* fix(tsdb): missing passing head option to wal/wbl write (#18113)

Signed-off-by: György Krajcsovits <[email protected]>

* feat(tsdb): allow using ST capable XOR chunks - retain format on read (#18013)

* feat(tsdb): allow appending to ST capable XOR chunk optionally

Only for float samples as of now.  Supports for in-order and out-of-order
samples.

Make sure that on readout the ST capable chunks are returned automatically.
When the chunks are returned as is, this is trivially true.
When a chunk needs to be re-coded due to deletion (tombstone) markers,
we take the encoding of the original chunk.
When a chunk needs to be created from overlapping chunks, we observe
whether ST is zero or not and create the new chunk based on that.

Signed-off-by: György Krajcsovits <[email protected]>

* fix test after merge

Signed-off-by: bwplotka <[email protected]>

* feat: RW2 sending ST support

Signed-off-by: bwplotka <[email protected]>

tmp

Signed-off-by: bwplotka <[email protected]>

* tests: test ST in a cheapest way possible

Signed-off-by: bwplotka <[email protected]>

* tests: add bench CLI recommended invokations

Signed-off-by: bwplotka <[email protected]>

* fixed tests after rebase

Signed-off-by: bwplotka <[email protected]>

* feat(chunkenc): replace xoroptst chunk encoding with xor2

XOR2 is based on #18238
With additional ST support.

Signed-off-by: György Krajcsovits <[email protected]>

* feat: add compliance RW sender test for agent

Signed-off-by: bwplotka <[email protected]>

* feat(agent): add support for appending ST

Signed-off-by: bwplotka <[email protected]>

* replace stray xoroptst words

Signed-off-by: György Krajcsovits <[email protected]>

* Apply suggestions from code review

Co-authored-by: Copilot <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>

* post merge conflict fixes

Signed-off-by: bwplotka <[email protected]>

* feat(tsdb): register st_storage in feature API

Register the st-storage feature flag in the feature registry via
the TSDB options, consistent with how other TSDB features like
exemplar_storage and delayed_compaction are registered.

Signed-off-by: György Krajcsovits <[email protected]>
Coded with Claude Sonnet 4.6.

* Document xor2-encoding feature flag

Signed-off-by: Carrie Edwards <[email protected]>

* Add xor2-encoding feature flag

Signed-off-by: Carrie Edwards <[email protected]>

* Update CHANGELOG

Signed-off-by: Carrie Edwards <[email protected]>

* Fix linting

Signed-off-by: Carrie Edwards <[email protected]>

* Remove setting of xor2 encoding option in db open

Signed-off-by: Carrie Edwards <[email protected]>

* Fix tests

Signed-off-by: Carrie Edwards <[email protected]>

* Fix linting

Signed-off-by: Carrie Edwards <[email protected]>

* Update feature flag description

Signed-off-by: Carrie Edwards <[email protected]>

* Update comments and feature flag description

Signed-off-by: Carrie Edwards <[email protected]>

* Update documentation for st-storage feature

Signed-off-by: Carrie Edwards <[email protected]>

* st: disconnect st-storage with xor2-encoding given planned experiments (#18316)

* st: disconnect st-storage with xor2-encoding given planned experiments

Signed-off-by: bwplotka <[email protected]>

* Update docs/feature_flags.md

Co-authored-by: George Krajcsovits <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>

* Update docs/feature_flags.md

Co-authored-by: George Krajcsovits <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>

* Update docs/feature_flags.md

Co-authored-by: George Krajcsovits <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>

* Update docs/feature_flags.md

Co-authored-by: George Krajcsovits <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>

---------

Signed-off-by: bwplotka <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>
Co-authored-by: George Krajcsovits <[email protected]>

---------

Signed-off-by: György Krajcsovits <[email protected]>
Signed-off-by: Ganesh Vernekar <[email protected]>
Signed-off-by: Bryan Boreham <[email protected]>
Signed-off-by: Aleksandr Smirnov <[email protected]>
Signed-off-by: Mohammad Abbasi <[email protected]>
Signed-off-by: matt-gp <[email protected]>
Signed-off-by: Ian Kerins <[email protected]>
Signed-off-by: SuperQ <[email protected]>
Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: Arve Knudsen <[email protected]>
Signed-off-by: ffgan <[email protected]>
Signed-off-by: Patryk Prus <[email protected]>
Signed-off-by: Owen Williams <[email protected]>
Signed-off-by: bwplotka <[email protected]>
Signed-off-by: 3Juhwan <[email protected]>
Signed-off-by: Sammy Tran <[email protected]>
Signed-off-by: Casie Chen <[email protected]>
Signed-off-by: Dan Cech <[email protected]>
Signed-off-by: kakabisht <[email protected]>
Signed-off-by: Jeanette Tan <[email protected]>
Signed-off-by: Divyansh Mishra <[email protected]>
Signed-off-by: Varun Chawla <[email protected]>
Signed-off-by: Martin Valiente Ainz <[email protected]>
Signed-off-by: Kyle Eckhart <[email protected]>
Signed-off-by: Julien Pivotto <[email protected]>
Signed-off-by: Matthieu MOREL <[email protected]>
Signed-off-by: Linas Medziunas <[email protected]>
Signed-off-by: Björn Rabenstein <[email protected]>
Signed-off-by: beorn7 <[email protected]>
Signed-off-by: Sayuru <[email protected]>
Signed-off-by: Matt <[email protected]>
Signed-off-by: Bartlomiej Plotka <[email protected]>
Signed-off-by: Carrie Edwards <[email protected]>
Co-authored-by: Bartlomiej Plotka <[email protected]>
Co-authored-by: Ganesh Vernekar <[email protected]>
Co-authored-by: Bryan Boreham <[email protected]>
Co-authored-by: Sasha <[email protected]>
Co-authored-by: Mohammad Abbasi <[email protected]>
Co-authored-by: matt-gp <[email protected]>
Co-authored-by: Ian Kerins <[email protected]>
Co-authored-by: SuperQ <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Arve Knudsen <[email protected]>
Co-authored-by: Julien <[email protected]>
Co-authored-by: ffgan <[email protected]>
Co-authored-by: Patryk Prus <[email protected]>
Co-authored-by: Ganesh Vernekar <[email protected]>
Co-authored-by: Joe Adams <[email protected]>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Owen Williams <[email protected]>
Co-authored-by: 3Juhwan <[email protected]>
Co-authored-by: Casie Chen <[email protected]>
Co-authored-by: Dan Cech <[email protected]>
Co-authored-by: hridyesh bisht <[email protected]>
Co-authored-by: zenador <[email protected]>
Co-authored-by: Divyansh Mishra <[email protected]>
Co-authored-by: Varun Chawla <[email protected]>
Co-authored-by: Martin Valiente Ainz <[email protected]>
Co-authored-by: Kyle Eckhart <[email protected]>
Co-authored-by: Matthieu MOREL <[email protected]>
Co-authored-by: Linas Medžiūnas <[email protected]>
Co-authored-by: Björn Rabenstein <[email protected]>
Co-authored-by: beorn7 <[email protected]>
Co-authored-by: Sayuru <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Carrie Edwards <[email protected]>
@roidelapluie

Copy link
Copy Markdown
Member Author

/prombench cancel

@prombot

prombot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Benchmark cancel is in progress.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants