Skip to content

fix: no set/hmap expiration during serialization #5349

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

Merged

Conversation

BorysTheDev
Copy link
Contributor

fixes: #5299

problem: we do expiration during serialization and save 0-size hmap or set that is incorrect
fix: avoid expiration during serialization and filter out expired hmap and set during loading

@BorysTheDev BorysTheDev requested review from romange and kostasrim June 23, 2025 15:26
@BorysTheDev BorysTheDev force-pushed the fix_data_expiring_for_hset_sset_during_serialization branch from 55bd977 to 1d64bb1 Compare June 23, 2025 15:27
@BorysTheDev BorysTheDev requested a review from adiholden June 23, 2025 15:33
@romange
Copy link
Collaborator

romange commented Jun 24, 2025

  1. The original issue was the correctness and not the performance problem during SAVE.
  2. The trade off with eliminating the SlowSize call is that we may serialize expired items.

There are counter examples towards both approaches, and the problem is not black and white, so it's important to identify "not well defined" issues and avoid arguing around them too much because it's not productive. Instead, once a not well defined issue is identified with cons and pros, the best course of actions is to list them in the PR in an objective way and ask the product owner (which is me) what's the best course of action.

I prefer to limit the scope of this PR to the original issue and fix only the loader part. In the long run we probably should fix the algorithmics behind the expired set and introduce sorted queue of expired times but it's not a priority.

@BorysTheDev
Copy link
Contributor Author

BorysTheDev commented Jun 24, 2025

  1. The original issue was the correctness and not the performance problem during SAVE.
  2. The trade off with eliminating the SlowSize call is that we may serialize expired items.

There are counter examples towards both approaches, and the problem is not black and white, so it's important to identify "not well defined" issues and avoid arguing around them too much because it's not productive. Instead, once a not well defined issue is identified with cons and pros, the best course of actions is to list them in the PR in an objective way and ask the product owner (which is me) what's the best course of action.

I prefer to limit the scope of this PR to the original issue and fix only the loader part. In the long run we probably should fix the algorithmics behind the expired set and introduce sorted queue of expired times but it's not a priority.

If we decide to leave expiration, I need to remove a key (if set/hmap is zero size) during the serialization process (to me it looks quite strange)

@romange
Copy link
Collaborator

romange commented Jun 24, 2025

Talked to @BorysTheDev offline and it seems his solution is the only one possible at the moment without doing too much changes in the saving code. The reason for that is that we assume immutability of the entries at lease at the interface level (see SliceSnapshot::SerializeEntry).

@BorysTheDev BorysTheDev force-pushed the fix_data_expiring_for_hset_sset_during_serialization branch from 1d64bb1 to 8792be6 Compare June 24, 2025 15:25
@BorysTheDev BorysTheDev force-pushed the fix_data_expiring_for_hset_sset_during_serialization branch 4 times, most recently from 48574b8 to 37559e1 Compare June 30, 2025 09:41
@BorysTheDev BorysTheDev requested a review from adiholden July 1, 2025 13:57
@BorysTheDev BorysTheDev force-pushed the fix_data_expiring_for_hset_sset_during_serialization branch from 37559e1 to 44f18ff Compare July 2, 2025 07:52
@BorysTheDev BorysTheDev requested a review from adiholden July 2, 2025 07:53
}
return;
item->load_config.append = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do you set it to false? only to check if to return in line 2588? instead can you check if append_res is valid there?

Copy link
Contributor Author

@BorysTheDev BorysTheDev Jul 2, 2025

Choose a reason for hiding this comment

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

This flag is also checked in CreateHMap and CreateSet. Unfortunately, the loading logic isn't straightforward, and it is hard to do changes.

// If the item has expired we may not find the key. Note if the key
// is found, but expired since we started loading, we still append to
// avoid an inconsistent state where only part of the key is loaded.
if (item->expire_ms == 0 || db_cntx.time_now_ms < item->expire_ms) {
// we avoid expiration for values inside RDB_TYPE_HASH_WITH_EXPIRY so the object can unexist
if ((item->val.rdb_type != RDB_TYPE_HASH_WITH_EXPIRY &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

I am not sure what is this check error, is it to detect internal error flow ?
anyway I think a better way to do this is instead of checking the rdb type is checking if items this item went through the errc::value_expired flow, so you can add to item a field to be set in the case where you have the expired items which is not error flow and than check if this filed is not set in this condition

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 tried this approach, but for one entity we can create a lot of items so this approach doesn;t work. This check helps detect the bugs, because during loading we shouldn't expire any streaming objects execpt hmap and sset

Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. The condition is very complicated. lets factor out it to two variables for readability:
    bool key_not_expired = item->expire_ms == 0 || db_cntx.time_now_ms < item->expire_ms
    bool set_not_expired = item->val.rdb_type != RDB_TYPE_SET_WITH_EXPIRY && ...
    then using key_not_expired && set_not_expired is more clear
  2. overriding item->load_config.append does not justify mutability. Create a temporary LoadConfig with overridden field and pass it to FromOpaque

Copy link
Collaborator

Choose a reason for hiding this comment

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

And add a comment of why you need to override it (i.e. to create a new key because the previous batches of items for that key were fully expired).

// If the item has expired we may not find the key. Note if the key
// is found, but expired since we started loading, we still append to
// avoid an inconsistent state where only part of the key is loaded.
if (item->expire_ms == 0 || db_cntx.time_now_ms < item->expire_ms) {
// we avoid expiration for values inside RDB_TYPE_HASH_WITH_EXPIRY so the object can unexist
if ((item->val.rdb_type != RDB_TYPE_HASH_WITH_EXPIRY &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. The condition is very complicated. lets factor out it to two variables for readability:
    bool key_not_expired = item->expire_ms == 0 || db_cntx.time_now_ms < item->expire_ms
    bool set_not_expired = item->val.rdb_type != RDB_TYPE_SET_WITH_EXPIRY && ...
    then using key_not_expired && set_not_expired is more clear
  2. overriding item->load_config.append does not justify mutability. Create a temporary LoadConfig with overridden field and pass it to FromOpaque

// If the item has expired we may not find the key. Note if the key
// is found, but expired since we started loading, we still append to
// avoid an inconsistent state where only part of the key is loaded.
if (item->expire_ms == 0 || db_cntx.time_now_ms < item->expire_ms) {
// we avoid expiration for values inside RDB_TYPE_HASH_WITH_EXPIRY so the object can unexist
if ((item->val.rdb_type != RDB_TYPE_HASH_WITH_EXPIRY &&
Copy link
Collaborator

Choose a reason for hiding this comment

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

And add a comment of why you need to override it (i.e. to create a new key because the previous batches of items for that key were fully expired).

@BorysTheDev BorysTheDev merged commit 14ee661 into main Jul 2, 2025
16 of 18 checks passed
@BorysTheDev BorysTheDev deleted the fix_data_expiring_for_hset_sset_during_serialization branch July 2, 2025 17:38
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.

rdb save crash
4 participants