Skip to content

tsdb: remove deleted series from WAL checkpoint without delay#12288

Closed
bboreham wants to merge 1 commit into
prometheus:mainfrom
bboreham:better-wal-truncate
Closed

tsdb: remove deleted series from WAL checkpoint without delay#12288
bboreham wants to merge 1 commit into
prometheus:mainfrom
bboreham:better-wal-truncate

Conversation

@bboreham

Copy link
Copy Markdown
Member

Remove the deleted map that retained deleted series for 3 head compaction cycles; it is not needed.

This should significantly improve the time to read the WAL, and reduce memory usage for remote-write.

Fixes #12286

To illustrate, pictured below is a Prometheus TSDB that has been collecting data since 10:00 UTC. WAL segments are named A, B, C, ...

           10:00        12:00
Head       ┌───────────────────┐
           └───────────────────┘
WAL        A- B- C- D- E-- F- G-

At approx 13:00, head compaction runs. A block is created from 10:00-12:00, and that data is dropped from the head. Any series which stopped before 12:00 is garbage-collected from the head.

A WAL checkpoint is created from the first two thirds of the segments, A-D. All samples before 12:00 are excluded. WAL segments A-D are removed from disk.

           10:00        12:00
Head                    ┌──────┐
                        └──────┘
Block      ┌────────────┐
           └────────────┘
WAL                    E-- F- G-
Checkpoint            X

There is no need to remember any series deleted from the head in the WAL checkpoint, since the condition to garbage-collect a series is that it has no samples after 12:00.

Remove the `deleted` map that retained deleted series for 3 head
compaction cycles; it is not needed.

This should significantly improve the time to read the WAL, and reduce
memory usage for remote-write.

To illustrate, pictured below is a Prometheus TSDB that has been
collecting data since 10:00 UTC. WAL segments are named A, B, C, ...

```
           10:00        12:00
Head       ┌───────────────────┐
           └───────────────────┘
WAL        A- B- C- D- E-- F- G-
```

At approx 13:00, head compaction runs.  A block is created from
10:00-12:00, and that data is dropped from the head. Any series which
stopped before 12:00 is garbage-collected from the head.

A WAL checkpoint is created from the first two thirds of the segments,
A-D. All samples before 12:00 are excluded. WAL segments A-D are removed
from disk.

```
           10:00        12:00
Head                    ┌──────┐
                        └──────┘
Block      ┌────────────┐
           └────────────┘
WAL                    E-- F- G-
Checkpoint            X
```

There is no need to remember any series deleted from the head in the WAL
checkpoint, since the condition to garbage-collect a series is that it
has no samples after 12:00.

Signed-off-by: Bryan Boreham <[email protected]>
@bboreham
bboreham requested a review from codesome as a code owner April 24, 2023 09:31
@bboreham
bboreham requested a review from jesusvazquez April 24, 2023 10:31

@jesusvazquez jesusvazquez left a comment

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.

I found this useful to understand why this deleted map existed dfed85e

There is no need to remember any series deleted from the head in the WAL checkpoint, since the condition to garbage-collect a series is that it has no samples after 12:00.

After reading @bboreham 's explanation and going through the code I think it makes sense to no longer track these.

I'm also thinking that if a sudden restart / out of memory where to happen, we could only rely on whatever series refs are on the WAL since these deleted memory map would also dissapear so to me there is no point in making the live WAL checkpointing more complex than the worst case scenario.

@bboreham

Copy link
Copy Markdown
Member Author

if a sudden restart / out of memory where to happen, we could only rely on whatever series refs are on the WAL since these deleted memory map would also dissapear

I think this scenario did not play out as you suggest. On restart the series would be loaded from the WAL checkpoint, then get removed by gc(), and (in the code before this PR) added back into deleted.

defer h.gc() // After loading the wal remove the obsolete data from the head.

prometheus/tsdb/head.go

Lines 1500 to 1502 in bae9a21

for ref := range deleted {
h.deleted[chunks.HeadSeriesRef(ref)] = last
}

(and they would be delayed another 3 cycles from being fully removed from the WAL)

@bboreham
bboreham marked this pull request as draft April 25, 2023 17:00
@bboreham

bboreham commented Apr 25, 2023

Copy link
Copy Markdown
Member Author

@codesome pointed out there can be samples in the 1/3 of WAL not being cleaned up, but still the series has been removed from the head.

In terms of the example, this means the samples came after 12:00 but also in the last 1/3 of the WAL.
This can happen if the rate of data slows down. Zooming in to make that easier to draw:

           10:00        11:00        12:00        13:00
Head       ┌───────────────────────────────────────┐
           └───────────────────────────────────────┘
WAL        A B C D E F G H- I-- J--- K--- L--- M---

There are 12 WAL segments from A to L, so 8 of them A-H would be deleted by truncation.
If the samples are in segment J, say, then we should not remove the series from the checkpoint.

@cstyan

cstyan commented Apr 25, 2023

Copy link
Copy Markdown
Member

I'm not understanding why the initial commit dfed85e was needed:

If all the samples are deleted for a series,
we should still keep the series in the WAL as
anything else reading the WAL will still care
about it in order to understand the samples.

what samples? we deleted them all for that series

On top of that, to counter act the scenario Ganesh pointed out in Bryan's latest comment, maybe we should consider checkpointing/truncating the WAL based on the overall time range rather than just WAL segment? it's just the segment index we use to decide whether we delete the segment or not: https://github.com/prometheus/prometheus/blob/main/tsdb/head.go#L1201-L1208

@bboreham

Copy link
Copy Markdown
Member Author

maybe we should consider checkpointing/truncating the WAL based on the overall time range

I think you can only do that by scanning the entire WAL and rewriting to remove deleted series.
This seems too expensive.

Note timestamps may be supplied by targets, so it's possible that each WAL segment has an overlapping time range with the one before.

@cstyan

cstyan commented Apr 26, 2023

Copy link
Copy Markdown
Member

Note timestamps may be supplied by targets, so it's possible that each WAL segment has an overlapping time range with the one before.

Okay yes, this definitely complicates things. I was thinking we could just introduce another record type that has metadata first sample and last sample timestamps as the last record in a segment, and check that record during the process to decide what segments to delete.

I think you can only do that by scanning the entire WAL and rewriting to remove deleted series.
This seems too expensive.

yes

@bboreham

Copy link
Copy Markdown
Member Author

Closing this; I think #12297 is workable now and a better solution will take time.

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.

WAL Checkpoint holds deleted series for 1 extra compaction cycle

3 participants