Skip to content

gc_worker: use async_snapshot instead of raw API in GC#13322

Merged
ti-chi-bot merged 48 commits intotikv:masterfrom
SpadeA-Tang:async_snapshot
Sep 5, 2022
Merged

gc_worker: use async_snapshot instead of raw API in GC#13322
ti-chi-bot merged 48 commits intotikv:masterfrom
SpadeA-Tang:async_snapshot

Conversation

@SpadeA-Tang
Copy link
Member

@SpadeA-Tang SpadeA-Tang commented Aug 22, 2022

What is changed and how it works?

Issue Number: Close #13319

What's Changed:

In the past, we have some raw APIs in Engine trait to avoid breaking hinerrnate regions when doing GC works: snapshot_on_kv_engine and modify_on_kv_engine.

Now, we have supported stale read and so we can use stale read + start_ts 0 to acquire the snapshot which is implemented in this PR. In addition, this PR provider a way to fetch the inner kv engine from the snapshot.

Related changes

  • PR to update pingcap/docs/pingcap/docs-cn:
  • Need to cherry-pick to the release branch

Check List

Tests

  • Unit test
  • Integration test

Side effects

  • Performance regression
    • Consumes more CPU
    • Consumes more MEM
  • Breaking backward compatibility

Release note

None

@ti-chi-bot
Copy link
Member

ti-chi-bot commented Aug 22, 2022

[REVIEW NOTIFICATION]

This pull request has been approved by:

  • BusyJay
  • tonyxuqqi

To complete the pull request process, please ask the reviewers in the list to review by filling /cc @reviewer in the comment.
After your PR has acquired the required number of LGTMs, you can assign this pull request to the committer in the list by filling /assign @committer in the comment to help you merge this pull request.

The full list of commands accepted by this bot can be found here.

Details

Reviewer can indicate their review by submitting an approval review.
Reviewer can cancel approval by submitting a request changes review.

@ti-chi-bot ti-chi-bot added do-not-merge/needs-linked-issue release-note Denotes a PR that will be considered when it comes time to generate release notes. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. contribution This PR is from a community contributor. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed do-not-merge/needs-linked-issue labels Aug 22, 2022
Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
@ti-chi-bot ti-chi-bot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Aug 22, 2022
@SpadeA-Tang
Copy link
Member Author

/test

Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
@SpadeA-Tang SpadeA-Tang changed the title WIP: use async_snapshot instead of raw API in GC gc_worker: use async_snapshot instead of raw API in GC Aug 24, 2022
@ti-chi-bot ti-chi-bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 24, 2022
Signed-off-by: SpadeA-Tang <[email protected]>
@ti-chi-bot ti-chi-bot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 2, 2022
self.pd_client.feature_gate().clone(),
Arc::new(self.region_info_accessor.clone()),
);
gc_worker
Copy link
Member

Choose a reason for hiding this comment

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

/cc @MyonKeminta PTAL

Copy link
Contributor

Choose a reason for hiding this comment

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

Starting of the gc_worker is delayed to init_servers function, just before start_auto_gc. The purpose seems to be getting the store id when starting the gc_worker. I think delaying the starting of gc_worker doesn't cause any severe problem if test passes, but I would suggest wrapping the three "start" functions into a single start_gc_worker function.

.debug_struct("Gc")
.field("start_key", &log_wrappers::Value::key(start_key))
.field("end_key", &log_wrappers::Value::key(end_key))
.field(
Copy link
Member

Choose a reason for hiding this comment

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

Why not .field("region", region)?

Copy link
Member Author

Choose a reason for hiding this comment

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

To be consistent with before. Other infomation in Region may not be needed.

Copy link
Member

Choose a reason for hiding this comment

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

Using region is OK.

// all regions of thoes keys.
// We return an iterator which yields items of `Key` and the region taht the key
// is located.
fn get_keys_in_regions(
Copy link
Member

Choose a reason for hiding this comment

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

This seems a bit over complicated. Why not just use 2 for loops?

for region in regions {
    let keys = get_keys_in_region(&keys, region);
    // process keys
}

}

#[test]
fn test_stale_read_with_ts0() {
Copy link
Member

Choose a reason for hiding this comment

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

If no failpoint is used, then it should be put in integration directory.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done


let store_id = 1;
let mut region = metapb::Region::default();
region.set_peers(RepeatedField::from_vec(vec![metapb::Peer {
Copy link
Member

Choose a reason for hiding this comment

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

You mean region.mut_peers().push(new_peer(store_id, 0))?

There are still many places using RepeatedField.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

futures = "0.3"
kvproto = { git = "https://github.com/pingcap/kvproto.git" }
pd_client = { path = "../pd_client", default-features = false }
protobuf = { version = "2.8", features = ["bytes"] }
Copy link
Member

Choose a reason for hiding this comment

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

This dependency should not be added.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Signed-off-by: SpadeA-Tang <[email protected]>
Signed-off-by: SpadeA-Tang <[email protected]>
let safe_point = safe_point.into();
for _ in 0..3 {
let ret = self.store.gc(self.ctx.clone(), safe_point);
let ret = self.store.gc(region, self.ctx.clone(), safe_point);
Copy link
Member

Choose a reason for hiding this comment

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

Why not just region.clone()? So you don't need to change the other function's definition.

Copy link
Member Author

Choose a reason for hiding this comment

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

The region may be changed.

Comment on lines +550 to +553
if region.is_none() {
return Ok(None);
};
let region = region.unwrap();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if region.is_none() {
return Ok(None);
};
let region = region.unwrap();
let Some(region) = region else { return Ok(None) };

.debug_struct("Gc")
.field("start_key", &log_wrappers::Value::key(start_key))
.field("end_key", &log_wrappers::Value::key(end_key))
.field(
Copy link
Member

Choose a reason for hiding this comment

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

Using region is OK.

@@ -247,40 +247,129 @@ struct KeysInRegions<R: Iterator<Item = Region>> {
}

impl<R: Iterator<Item = Region>> Iterator for KeysInRegions<R> {
Copy link
Member

Choose a reason for hiding this comment

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

This can be removed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

fn get_regions_for_gc(
store_id: u64,
keys: &Vec<Key>,
region_or_provider: Either<Region, Arc<dyn RegionInfoProvider>>,
Copy link
Member

Choose a reason for hiding this comment

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

It can just take Arc<dyn RegionInfoProvider>. If it's Either::Left, there is no need to call this method.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Signed-off-by: SpadeA-Tang <[email protected]>
// Return regions that keys are related to.
fn get_regions_for_gc(
store_id: u64,
keys: &Vec<Key>,
Copy link
Member

Choose a reason for hiding this comment

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

Should be &[Key].

Ok(regions)
} else {
// We only have one key.
let key = keys.first().unwrap().as_encoded();
Copy link
Member

Choose a reason for hiding this comment

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

Should be just keys[0].as_encoded().

let end = keys.last().unwrap().as_encoded();
let regions = box_try!(region_provider.get_regions_in_range(start, end))
.into_iter()
.filter(move |r| find_peer(r, store_id).is_some())
Copy link
Member

Choose a reason for hiding this comment

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

move is unnecessary.


match box_try!(rx.recv()) {
Some(region) => Ok(region),
None => unreachable!(),
Copy link
Member

Choose a reason for hiding this comment

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

Why is it unreachable? What if there is no replicas on the node?

MvccReader::new(snapshot, Some(ScanMode::Forward), false)
let (mut handled_keys, mut wasted_keys) = (0, 0);
let mut regions = match region_or_provider {
Either::Left(region) => vec![region].into_iter().peekable(),
Copy link
Member

Choose a reason for hiding this comment

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

You don't need that. Vec<Region>[0] is the same as Vec.into_iter().peekable().peek().

let (mut handled_keys, mut wasted_keys) = (0, 0);
// First item is fetched to initialize the reader and kv_engine
let region = regions.peek();
if region.is_none() {
Copy link
Member

Choose a reason for hiding this comment

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

regions.is_empty() is more straightforward.

gc_info.is_completed = true;
let mut keys = keys.into_iter().peekable();
for region in regions {
if !first_iteration {
Copy link
Member

Choose a reason for hiding this comment

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

Why not

for region in regions {
    let (reader, kv_engine) = self.create_reader()?;
    let txn = Self::new_txn();
    xxxx;
    Self::flush_txn();
}

When one tablet per region, you can't flush transaction across tablet.

Copy link
Member Author

Choose a reason for hiding this comment

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

But it can also have single-rocksdb setting.

Copy link
Member

Choose a reason for hiding this comment

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

Interesting. In the future, it may need to send the modifies to apply worker by region instead.

SpadeA-Tang and others added 3 commits September 2, 2022 21:52
Copy link
Member

@BusyJay BusyJay left a comment

Choose a reason for hiding this comment

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

Rest LGTM

let mut gc_info = GcInfo::default();
let mut keys = keys.into_iter().peekable();
for region in regions {
for region in regions.into_iter() {
Copy link
Member

Choose a reason for hiding this comment

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

Why? Vec can be used directly.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

Comment on lines +274 to +278
let key = keys.peek();
if key.is_none() {
break;
}
let key = key.unwrap().as_encoded().as_slice();
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let key = keys.peek();
if key.is_none() {
break;
}
let key = key.unwrap().as_encoded().as_slice();
let Some(key) = keys.peek() else { break };

}
}

pub fn db(&self) -> Arc<DB> {
Copy link
Member

Choose a reason for hiding this comment

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

Not necessary anymore, people can use as_inner.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

let (locks, _) = reader
.scan_locks(Some(start_key), None, |l| l.ts <= max_ts, limit)
.map_err(TxnError::from_mvcc)?;
let regions = box_try!(regions_provider.get_regions_in_range(start_key.as_encoded(), &[]))
Copy link
Member

Choose a reason for hiding this comment

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

We need to evaluate the performance impact of changing from one seek to multiple seek.

Copy link
Contributor

Choose a reason for hiding this comment

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

As Green GC is going to be deprecated, I think we don't need to concern about it too much.
Maybe it is the time to deprecate it now, since only scanning regions found in region_info_provider may possibly affect the scanning's correctness.

Copy link
Member

Choose a reason for hiding this comment

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

Indeed.

let snap = self.get_snapshot(self.store_id, &region)?;
let mut reader = MvccReader::new(snap, Some(ScanMode::Forward), false);
let (locks_this_region, _) = reader
.scan_locks(Some(&start_key), None, |l| l.ts <= max_ts, limit)
Copy link
Member

Choose a reason for hiding this comment

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

/cc @lhy1024 Will it impact the statistics? As there will be many scan ops and end key is always None.

Copy link
Member Author

Choose a reason for hiding this comment

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

Scan is also limited by region boundries of snapshot.

Copy link
Contributor

Choose a reason for hiding this comment

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

If this pr only affects gc, it has no effect on hotspot statistics, because pd does not process gc-related statistics for the time being

Signed-off-by: SpadeA-Tang <[email protected]>
@ti-chi-bot ti-chi-bot added status/LGT2 Indicates that a PR has LGTM 2. and removed status/LGT1 Indicates that a PR has LGTM 1. labels Sep 5, 2022
@MyonKeminta
Copy link
Contributor

/merge

@ti-chi-bot
Copy link
Member

@MyonKeminta: It seems you want to merge this PR, I will help you trigger all the tests:

/run-all-tests

You only need to trigger /merge once, and if the CI test fails, you just re-trigger the test that failed and the bot will merge the PR for you after the CI passes.

If you have any questions about the PR merge process, please refer to pr process.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@ti-chi-bot
Copy link
Member

This pull request has been accepted and is ready to merge.

DetailsCommit hash: bc9deea

@ti-chi-bot ti-chi-bot added the status/can-merge Indicates a PR has been approved by a committer. label Sep 5, 2022
@ti-chi-bot
Copy link
Member

@SpadeA-Tang: Your PR was out of date, I have automatically updated it for you.

At the same time I will also trigger all tests for you:

/run-all-tests

If the CI test fails, you just re-trigger the test that failed and the bot will merge the PR for you after the CI passes.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository.

@ti-chi-bot ti-chi-bot merged commit 7d36f34 into tikv:master Sep 5, 2022
@ti-chi-bot ti-chi-bot added this to the Pool milestone Sep 5, 2022

pub fn start_auto_gc<S: GcSafePointProvider, R: RegionInfoProvider + Clone + 'static>(
&self,
kv_engine: &E::Local,
Copy link
Member

Choose a reason for hiding this comment

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

@SpadeA-Tang Could you help explain a bit why this is necessary?

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay... I have remembered why this change exists. I tried to remove the kv_engine() from Engine trait in some point of time but I gave up this way later without changeing it back. I will propose another PR for reverting it.

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

Labels

contribution This PR is from a community contributor. release-note-none Denotes a PR that doesn't merit a release note. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. status/can-merge Indicates a PR has been approved by a committer. status/LGT2 Indicates that a PR has LGTM 2.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use stale read to acquire snapshot instead of raw APIs

7 participants