Skip to content
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

Option to fail a request as incomplete when skipping too many internal keys #2000

Closed
wants to merge 15 commits into from

Conversation

sagar0
Copy link
Contributor

@sagar0 sagar0 commented Mar 17, 2017

Operations like Seek/Next/Prev sometimes take too long to complete when there are many internal keys to be skipped. Adding an option, max_skippable_internal_keys -- which could be used to set a threshold for the maximum number of keys that can be skipped, will help to address these cases where it is much better to fail a request (as incomplete) than to wait for a considerable time for the request to complete.

This feature -- to fail an iterator seek request as incomplete, is disabled by default when max_skippable_internal_keys = 0. It is enabled only when max_skippable_internal_keys > 0.

This feature is based on the discussion mentioned in the PR #1084.

@siying
Copy link
Contributor

siying commented Mar 17, 2017

@sagar0 make sure you add an entry in HISTORY.md. That will convert to release notes.

@sagar0
Copy link
Contributor Author

sagar0 commented Mar 17, 2017

@siying sure, will do.

@facebook-github-bot
Copy link
Contributor

@sagar0 updated the pull request - view changes

@sagar0 sagar0 changed the title Option to fail a request as incomplete when skipping too many tombstoned keys Option to fail a request as incomplete when skipping too many internal keys Mar 22, 2017
@facebook-github-bot
Copy link
Contributor

@sagar0 updated the pull request - view changes

@facebook-github-bot
Copy link
Contributor

@sagar0 updated the pull request - view changes

@facebook-github-bot
Copy link
Contributor

@sagar0 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

db/db_iter.cc Outdated
} else {
num_tombstones_skipped_++;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Since the option name is max_tombstones_skip_in_iterations, as discussed offline, we should have a better name for this since it includes every iterator no matter whether it is tombstone or not.

db/db_iter.cc Outdated
// } else {
// num_tombstones_skipped_++;
// }

Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, which should be moved to line 769 I think.

db/db_iter.cc Outdated
} else {
num_internal_keys_skipped_++;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this part should be on line 789. The first time we enter this loop we don't know whether the key will be skipped.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, how to deal with too many merge_operands in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a reasonable suggestion. I believe I added this code snippet here as the other check (in line 742) is also here, and I wanted to keep both of them close together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lightmark should there be something different done for merges? I am assuming that they should also be handled in the same way. We should still fail and return an incomplete status if too many keys are being looked for getting the value of a merge. Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds like a reasonable behavior for merge operands

db/db_iter.cc Outdated
if (TooManyInternalKeysSkipped()) {
return;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

These lines are not necessary since you set valid_ = false; already in TooManyInternalKeysSkipped. So line 700 will break;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I understand the code correctly, valid_'s value need not always be the same as iter_->Valid() value. In this code snippet, if valid_ is set to false in TooManyInternalKeysSkipped(), we will not break in 701 as we never enter the if block (iter_->Valid() need not be false). Hence a return here.
In fact, some of my unit tests check this scenario.

Copy link
Contributor

Choose a reason for hiding this comment

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

You are right. I thought it was Valid() not iter_->Valid()

db/db_iter.cc Outdated
}
return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe if would be better to move num_internal_keys_skipped_++; before line 969.
Then you can do this instead an if...else...

if (TooManyInternalKeysSkipped()) {
    return;
}

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 definitely did consider this, and I would have loved to done the exact same thing which you suggested, but it will not work in the reverse iteration flow as we will end up double counting in a few cases.
The Reason being:
TooManyInternalKeysSkipped is called from both PrevInternal and FindValueForCurrentKey. FindValueForCurrentKey is in turn called from PrevInternal. If the counter is incremented in FindValueForCurrentKey, it should not be incremented again in PrevInternal. One of my unit tests ran into this problem of double counting, and I had to fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure. This comment has the prerequisite that the first comment is valid.

Copy link
Contributor

@lightmark lightmark left a comment

Choose a reason for hiding this comment

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

LGTM. Just consider how to cope with merge in FindValueForCurrentKey

db/db_iter.cc Outdated
if (TooManyInternalKeysSkipped()) {
return;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

You are right. I thought it was Valid() not iter_->Valid()

db/db_iter.cc Outdated
}
return false;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure. This comment has the prerequisite that the first comment is valid.

@@ -106,7 +106,8 @@ class DBIter: public Iterator {
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
const Slice* iterate_upper_bound = nullptr,
bool prefix_same_as_start = false, bool pin_data = false,
bool total_order_seek = false)
bool total_order_seek = false,
uint64_t max_skippable_internal_keys = 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

We keep passing new arguments to the Iterator, I think we should simply pass the ReadOptions. this should be a refactor that we should consider to do in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in #2116.

@@ -390,6 +400,12 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
break;
}

if (TooManyInternalKeysSkipped()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

let's look into if it's possible to have it look like this

if (BumpInternalKeysSkipped()) {
  return false;
}

we can even have a statistics counter inside this function that will report the total number of internal keys that we need to skip

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved counter-incrementing to be inside the function.
I have not added statistics counters in this PR, but I'll look at adding them in a separate PR.

db/db_iter.cc Outdated
} else {
num_internal_keys_skipped_++;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

This sounds like a reasonable behavior for merge operands

@facebook-github-bot
Copy link
Contributor

@sagar0 updated the pull request - view changes - changes since last import

@facebook-github-bot
Copy link
Contributor

@sagar0 has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link
Contributor

@IslamAbdelRahman IslamAbdelRahman left a comment

Choose a reason for hiding this comment

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

LGTM, let's wait for the tests to pass then land it

@sagar0 sagar0 deleted the max-tombstones branch April 4, 2017 17:35
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.

5 participants