Skip to content

Expose a metric tracking the oldest timestamp of unshipper block in ingester #3705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

pracucci
Copy link
Contributor

What this PR does:
Currently, there's no way to alert if any compacted block is not shipped from ingester to storage within a reasonable time. In this PR I'm proposing to introduce a new metric cortex_ingester_oldest_unshipped_block_timestamp_seconds tracking the unix timestamp of the oldest TSDB block not shipped to the storage yet.

Which issue(s) this PR fixes:
N/A

Checklist

  • Tests updated
  • Documentation added
  • CHANGELOG.md updated - the order of entries should be [CHANGE], [FEATURE], [ENHANCEMENT], [BUGFIX]

Copy link
Contributor

@pstibrany pstibrany left a comment

Choose a reason for hiding this comment

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

LGTM, but I'd like to see your take on caching the value instead.

// Read the list of shipper blocks. If the shipper metadata file doesn't exist
// (eg. because the shipper hasn't run yet) we're considering it as if no block
// has been shipped.
shipped, err := u.getShippedBlocks()
Copy link
Contributor

Choose a reason for hiding this comment

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

getShippedBlocks is used twice, each time we build a map from it. Perhaps getShippedBlocks should build and return map directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

blocksToDelete() doesn't build a map of out getShipperBlocks(). It just iterates over it.


oldest := uint64(0)
for _, db := range i.TSDBState.dbs {
if ts, err := db.getOldestUnshippedBlockTime(); err == nil && (oldest == 0 || ts < oldest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of computing this value on each metric collection, should we cache it and update after each call to the shipper? As it is now, each metric collection needs to read shipper json file for each tenant, which could be slow if there are many tenants.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can benchmark it and then we can decide if extra complexity is worth it. But agree checking it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've written a simple benchmark (commit) and on my MacBook it takes 31μs/op. If you have 1K tenants, it would be 31ms. I've the feeling caching for it, at this stage, would just be premature optimisation.

BenchmarkUserTSDB_getOldestUnshippedBlockTime-12    	   37930	     30898 ns/op	    4486 B/op	      26 allocs/op

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a valid benchmark? On a busy server getOldestUnshippedBlockTime will not benefit from heavy caching of all IO operations as much as it does in this standalone test.

I don't think it would be a premature optimization, just a better approach. For 1K tenants, current approach needs to open/read/close 1000 files on each metric collection (=3000 syscalls). Instead we could simply read one atomic variable per tenant, and update it only when it can actually change. I don't see it as "premature optimization".

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, I share concern this could be pretty inefficient for large number of tenants.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, got it. I pushed a change to keep the shipped blocks in memory (they're tiny) and reload them each time any block is uploaded. I've also took the opportunity to improve the tests to not use a mocked shipper but the real one backed by a filesystem bucket.

What do you think?

@pracucci pracucci force-pushed the expose-metric-with-oldest-unshipped-block-timestamp branch from ffab7ac to 9447e9a Compare January 21, 2021 14:54
Copy link
Contributor

@ranton256 ranton256 left a comment

Choose a reason for hiding this comment

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

Thanks for doing this, functionally, in terms of the metric exported it looks good to me, but I second pstibrany's concern about the cost of calculating the metric at scrape time so would prefer the alternative implementation suggested.


oldest := uint64(0)
for _, db := range i.TSDBState.dbs {
if ts, err := db.getOldestUnshippedBlockTime(); err == nil && (oldest == 0 || ts < oldest) {
Copy link
Contributor

Choose a reason for hiding this comment

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

+1, I share concern this could be pretty inefficient for large number of tenants.

@@ -68,6 +68,7 @@
* [ENHANCEMENT] Distributor: change the error message returned when a received series has too many label values. The new message format has the series at the end and this plays better with Prometheus logs truncation. #3718
- From: `sample for '<series>' has <value> label names; limit <value>`
- To: `series has too many labels (actual: <value>, limit: <value>) series: '<series>'`
* [ENHANCEMENT] Ingester: exposed metric `cortex_ingester_oldest_unshipped_block_timestamp_seconds`, tracking the unix timestamp of the oldest TSDB block not shipped to the storage yet. #3705
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We've just released 1.7.0-rc.0. Could you rebase master and move the CHANGELOG entry under the master / unreleased section, please?

@pracucci pracucci force-pushed the expose-metric-with-oldest-unshipped-block-timestamp branch from 9447e9a to 6318f48 Compare January 22, 2021 13:16
Copy link
Contributor

@ranton256 ranton256 left a comment

Choose a reason for hiding this comment

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

Thanks for the update.

shipTicker := time.NewTicker(i.cfg.BlocksStorageConfig.TSDB.ShipInterval)
// We add a slight jitter to make sure that if the head compaction interval and ship interval are set to the same
// value they don't clash (if they both continuously run at the same exact time, the head compaction may not run
// because can't successfully cas the state).
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you mean by "cas the state"? Not sure if I'm confused about a term I don't know or a typo ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By cas I mean "Compare And Swap" (we have a function called casState()). Maybe "CAS" (upper case) is more clear? Should I explode it to "compare and swap"?

Copy link
Contributor

@pstibrany pstibrany left a comment

Choose a reason for hiding this comment

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

👍 Thanks!

Also nice cleanup in the tests!

…ngester

Signed-off-by: Marco Pracucci <marco@pracucci.com>
Signed-off-by: Marco Pracucci <marco@pracucci.com>
Signed-off-by: Marco Pracucci <marco@pracucci.com>
Signed-off-by: Marco Pracucci <marco@pracucci.com>
Signed-off-by: Marco Pracucci <marco@pracucci.com>
@pracucci pracucci force-pushed the expose-metric-with-oldest-unshipped-block-timestamp branch from 74f2715 to 2866e6b Compare January 25, 2021 10:26
Signed-off-by: Marco Pracucci <marco@pracucci.com>
@pracucci pracucci merged commit f72ac74 into cortexproject:master Jan 25, 2021
@pracucci pracucci deleted the expose-metric-with-oldest-unshipped-block-timestamp branch January 25, 2021 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants