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

feat(hash): Support hash field expiration #2402

Open
wants to merge 4 commits into
base: unstable
Choose a base branch
from

Conversation

jjz921024
Copy link
Contributor

@jjz921024 jjz921024 commented Jul 11, 2024

This pr want to close #2269

  1. Encoding
    To implement hash expiration by field level, we encode the expiration time into the first 8 bytes of value.
    For example:

                         +-------------------------+
    key|version|field => | expire (8 byte) | value |
                         +-------------------------+
    

    And we use the second bit of the flags in metadata to indicate whether encode the expiration into value.
    If all the fields in the hash have no encode expiration time, the flags will be 1 0 0 0 | 0 0 1 0.
    If any field in the hash is encoded into the expiration time, the flags will be 1 1 0 0 | 0 0 1 0.

  2. Flags conversion
    For the HEXPIRE command, it will set an expiration on one or more fields.
    If we execute hexpire command on a hash key that has not been set to expire, this will set the secode bit of flags and encode expiration for all fields (fields that not appear in the command will be set to 0)

  3. Add commands
    According to https://redis.io/docs/latest/commands add a series of related commands:

    • HEXPIRE , HPEXPIRE , HEXPIREAT , HPEXPIREAT
    • HEXPIRETIME , HPEXPIRETIME , HTTL , HPTTL
    • HPERSIST

@jjz921024 jjz921024 changed the title Support hash field expiration feat(hash): Support hash field expiration Jul 11, 2024
@jjz921024 jjz921024 marked this pull request as ready for review July 11, 2024 12:36
@PragmaTwice
Copy link
Member

PragmaTwice commented Jul 11, 2024

And we use the second bit of the flags in metadata to indicate whether encode the expiration into value.

As we specified in https://kvrocks.apache.org/community/data-structure-on-rocksdb#encoding-version-and-data-type, the first and second (and maybe more bits from the MSB) of flags is used to represent the "encoding version". flags 1 1 0 0 | 0 0 1 0 indicates that the encoding version is 3.

So it should not be used for field expiration. (Also, not all data types need the field expiration feature.)

Instead, you can add a new enum value of data type, or add a new field in the metadata of hash.

@git-hulk
Copy link
Member

@jjz921024 Thanks for your contributions. As @PragmaTwice mentioned, we cannot put the expired flag in the version field and we also need to adopt the SubkeyFilter in compact_filter.cc to recycle expired subkeys.

@jjz921024
Copy link
Contributor Author

jjz921024 commented Jul 12, 2024

we also need to adopt the SubkeyFilter in compact_filter.cc to recycle expired subkeys.

@git-hulk Thanks for you reminding. I have added this feature in latest commit.

About encoding, my previous thought was to use each bit for a different meaning, rather than as an incremental version.
For example:

  • For all type, the first bit indicate use 64bit or 32bit encoding
  • For hash type, the second bit of the flags to indicate whether encode the expiration into value (not affect for other types)

I see this issues #2292. Maybe we should finish it first?

@PragmaTwice
Copy link
Member

PragmaTwice commented Jul 12, 2024

For all type, the first bit indicate use 64bit or 32bit encoding
For hash type, the second bit of the flags to indicate whether encode the expiration into value (not affect for other types)

No. It's too ad hoc and will introduce additional maintanance effort. (the encoding of HASH will be inconsistent with other types, which brings huge understanding, maintanance cost and additional code logic)

And also, it breaks our whole design of "encoding version", e.g. HASH cannot have encoding version larger than 1.

You can consider these options, as I mentioned before.

Instead, you can add a new enum value of data type, or add a new field in the metadata of hash.


-1 for this change if no further commits.

@jjz921024
Copy link
Contributor Author

@PragmaTwice Got it, i will change it.

src/commands/cmd_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/storage/redis_db.cc Outdated Show resolved Hide resolved
src/storage/redis_metadata.h Outdated Show resolved Hide resolved
src/storage/redis_db.cc Outdated Show resolved Hide resolved
@torwig
Copy link
Contributor

torwig commented Jul 14, 2024

@ jjz921024 Please let me know if I'm wrong. If the data structure contains at least 1 key that can be expired, we set a special bit to true and later check it.

src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.h Outdated Show resolved Hide resolved
src/types/redis_hash.h Outdated Show resolved Hide resolved
src/types/redis_hash.h Outdated Show resolved Hide resolved
src/types/redis_hash.h Outdated Show resolved Hide resolved
@jjz921024
Copy link
Contributor Author

@ jjz921024 Please let me know if I'm wrong. If the data structure contains at least 1 key that can be expired, we set a special bit to true and later check it.

Yes. Due to the expiration time is encoded in the field, the time complexity of some commands changes from O(1) to O(n), such as type and hlen. If we can check this flag before execution, then the some commands complexity will remains the same when
this feature is not used

@jjz921024
Copy link
Contributor Author

jjz921024 commented Jul 16, 2024

For all type, the first bit indicate use 64bit or 32bit encoding
For hash type, the second bit of the flags to indicate whether encode the expiration into value (not affect for other types)

No. It's too ad hoc and will introduce additional maintanance effort. (the encoding of HASH will be inconsistent with other types, which brings huge understanding, maintanance cost and additional code logic)

And also, it breaks our whole design of "encoding version", e.g. HASH cannot have encoding version larger than 1.

You can consider these options, as I mentioned before.

Instead, you can add a new enum value of data type, or add a new field in the metadata of hash.

-1 for this change if no further commits.

@PragmaTwice Hi, I fix it about encoding version. and add a new field in the metadata of hash to indicate whether the expiration time of field is encoded. please review. thank

src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/commands/cmd_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/storage/redis_metadata.h Outdated Show resolved Hide resolved
src/types/redis_hash.h Outdated Show resolved Hide resolved
src/storage/compact_filter.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
src/types/redis_hash.cc Outdated Show resolved Hide resolved
@jjz921024
Copy link
Contributor Author

jjz921024 commented Jul 19, 2024

@git-hulk Hi, I had renamed the decodeFieldAndTTL to decodeExpireFromValue, encodeFieldAndTTL to encodeExpireToValue. And rewrite the IsFieldExpired() to make it clearer. please review.

src/config/config.h Outdated Show resolved Hide resolved
@PragmaTwice
Copy link
Member

Seems we need to solve the conflict first?

@jjz921024
Copy link
Contributor Author

Seems we need to solve the conflict first?

rebase this pr on the latest commit of unstable branch.

@jjz921024
Copy link
Contributor Author

Seems there are failure in CI. I will check it up

Copy link

@git-hulk
Copy link
Member

@jjz921024 I will take another pass in a few days, thanks for your contribution.

@jjz921024 jjz921024 force-pushed the hfe branch 2 times, most recently from 0704688 to 072c6b1 Compare September 2, 2024 10:27
@ltagliamonte
Copy link

@jjz921024 @git-hulk @PragmaTwice I'm looking forward to this feature, is there anything I can help with to help here?

@PragmaTwice
Copy link
Member

@jjz921024 @git-hulk @PragmaTwice I'm looking forward to this feature, is there anything I can help with to help here?

Thank you for your input. I'll take a look at this weekend.

@jjz921024
Copy link
Contributor Author

rebase this branch on the HEAD

Comment on lines 103 to 112
// if type is hash, we still need to check if the all of fields expired.
if (metadata->Type() == kRedisHash) {
HashMetadata hash_metadata(false);
s = hash_metadata.Decode(*raw_value);
if (!s.ok()) return s;
redis::Hash hash_db(storage_, namespace_);
if (!hash_db.ExistValidField(ctx, ns_key, hash_metadata)) {
return rocksdb::Status::NotFound("no element found");
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This will make almost all operation of HASH inefficient, we should consider how to do this in another way.

Copy link
Member

@PragmaTwice PragmaTwice Dec 21, 2024

Choose a reason for hiding this comment

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

It requires at least O(N) IO accesses for every HASH operation. And if you think about it: not all operations require to check that all elements are expired before the actual operation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It seems that only hset and hmset does not need to check the expire of the field?But we don't know the the command type in the GetMetadata()

@aleksraiden
Copy link
Contributor

Awesome work, @jjz921024

Maybe we need some benchmark - before include this feature and after? Тo test the impact of new logic on basic HSET's command (and prevent or minimize it)?

@jjz921024
Copy link
Contributor Author

@git-hulk @ltagliamonte Hi, I don't know how to commit code on the new pr #2717 , so I just keep working here.

For the first point you mentioned on #2717 (comment), I had finish. please review.

And the second point, do you mean that the ExistValidField() function should contain the following code? This code is repeated many times. I think the ExistValidField() is a dedicated function to check if it has any valid fields.

HashMetadata hash_metadata(false);
s = hash_metadata.Decode(rocksdb::Slice(pin_values[i].data(), pin_values[i].size()));
if (!s.ok()) continue;
redis::Hash hash_db(storage_, namespace_);

@jjz921024
Copy link
Contributor Author

Awesome work, @jjz921024

Maybe we need some benchmark - before include this feature and after? Тo test the impact of new logic on basic HSET's command (and prevent or minimize it)?

@aleksraiden Hi, After optimization of 473db0c, the expire fields should have no effect on HSET command. Should we add some bench test for read command of hash type (like hgetall)?

@aleksraiden
Copy link
Contributor

Awesome work, @jjz921024
Maybe we need some benchmark - before include this feature and after? Тo test the impact of new logic on basic HSET's command (and prevent or minimize it)?

@aleksraiden Hi, After optimization of 473db0c, the expire fields should have no effect on HSET command. Should we add some bench test for read command of hash type (like hgetall)?

I have a test by standard redis-bench yesterday (before your commit) - and at quick look is there NO any impact to performance. But standard test looks only to GET/HSET as I see.

Great job!

@git-hulk
Copy link
Member

@git-hulk @ltagliamonte Hi, I don't know how to commit code on the new pr #2717 , so I just keep working here.

@jjz921024 Perhaps you can suggest your change in the new PR due to this PR is overload for discussing. What do you think?

I have a test by standard redis-bench yesterday (before your commit) - and at quick look is there NO any impact to performance. But standard test looks only to GET/HSET as I see.

@aleksraiden Great!

@jjz921024
Copy link
Contributor Author

jjz921024 commented Jan 12, 2025

Perhaps you can suggest your change in the new PR due to this PR is overload for discussing. What do you think?

I'm pleasure to do it and I will try my best to finish this feature if i can.

@git-hulk
Copy link
Member

I'm pleasure to do it and I will try my best to finish this feature if i can.

@jjz921024 Thank you! We will take care of PR #2717 to keep the original commit information while merging.

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.

Add support of hash field expiration
6 participants