[NEW] Optimized Hash Member Scan
The problem/use-case that the feature addresses
The existing machinery for doing a scan of a HASH key is inefficient when used by the search module. The inefficiency is that both the member name and value are copied into newly allocated SDS strings before they are passed into the scan callback routine. This is inefficient because all that search is going to do with these two strings is to immediately put them into a reply.
This situation happens by default on every FT.SEARCH query, as the default is to return all values of a key.
Description of the feature
What is desired is a new module API that returns pointers and lengths to the members and data of a hash key without memory allocation. The assumption is that the client understands that these will become invalid if the key is mutated or control is returned to the core (module returns or unlocks the mainthread).
Alternatives you've considered
- Alternative Implementation 1a (preferred)
In this implementation, a single API call is performed and an array is passed in. The assumption is that the module first invokes the ValkeyModule_ValueLength API to determine the number of members of the hash (this will be O(1) except when the underlying key is a listpack with more than 2^16 members) and the pre-allocates an array large enough.
An API could look like this:
struct ValkeyModuleHashMember {
const char *member_name_ptr;
size_t member_name_len;
const char *data_ptr;
size_t data_len;
};
size_t VM_GetHashMembers(ValkeyModule_Key *key, ValkeyModuleHashMember *members, size_t num_members);
The return value would be the number of consumed entries in the array. Special return values would indicate errors like not enough space or this wasn't a hash key.
- Alternative Implementation 1b
Same as alternative 1a, except instead of introducing a new structure to return the data, four separate arrays are passed in, i.e.:
size_t VM_GetHashMembers(ValkeyModule_Key *key, const char **member_name_ptrs, size_t *member_name_sizes, const char **data_ptrs, size_t *data_sizes, size_t array_sizes);- Alternative 2
This alternative would follow the existing template of a scan with callback. Except that the values passed into the callback would be the raw pointers to the underlying data instead of allocated module strings as in the current interface.
This alternative is not preferred primarily because of the complexity of the implementation and the difficulty of using it.
Source: valkey-io/valkey