| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "cluster/cluster_defs.h" |
| #include "redis_metadata.h" |
| #include "storage.h" |
| |
| namespace redis { |
| |
| /// SORT_LENGTH_LIMIT limits the number of elements to be sorted |
| /// to avoid using too much memory and causing system crashes. |
| /// TODO: Expect to expand or eliminate SORT_LENGTH_LIMIT |
| /// through better mechanisms such as memory restriction logic. |
| constexpr uint64_t SORT_LENGTH_LIMIT = 512; |
| |
| struct SortArgument { |
| std::string sortby; // BY |
| bool dontsort = false; // DONT SORT |
| int offset = 0; // LIMIT OFFSET |
| int count = -1; // LIMIT COUNT |
| std::vector<std::string> getpatterns; // GET |
| bool desc = false; // ASC/DESC |
| bool alpha = false; // ALPHA |
| std::string storekey; // STORE |
| }; |
| |
| struct RedisSortObject { |
| std::string obj; |
| std::variant<double, std::string> v; |
| |
| /// SortCompare is a helper function that enables `RedisSortObject` to be sorted based on `SortArgument`. |
| /// |
| /// It can assist in implementing the third parameter `Compare comp` required by `std::sort` |
| /// |
| /// \param args The basis used to compare two RedisSortObjects. |
| /// If `args.alpha` is false, `RedisSortObject.v` will be taken as double for comparison |
| /// If `args.alpha` is true and `args.sortby` is not empty, `RedisSortObject.v` will be taken as string for comparison |
| /// If `args.alpha` is true and `args.sortby` is empty, the comparison is by `RedisSortObject.obj`. |
| /// |
| /// \return If `desc` is false, returns true when `a < b`, otherwise returns true when `a > b` |
| static bool SortCompare(const RedisSortObject &a, const RedisSortObject &b, const SortArgument &args); |
| }; |
| |
| /// Database is a wrapper of underlying storage engine, it provides |
| /// some common operations for redis commands. |
| class Database { |
| public: |
| static constexpr uint64_t RANDOM_KEY_SCAN_LIMIT = 60; |
| |
| explicit Database(engine::Storage *storage, std::string ns = ""); |
| /// Parsing metadata with type of `types` from bytes, the metadata is a base class of all metadata. |
| /// When parsing, the bytes will be consumed. |
| [[nodiscard]] rocksdb::Status ParseMetadataWithStats(RedisTypes types, Slice *bytes, Metadata *metadata); |
| // ParseMetadata behaves the same as ParseMetadataWithStats, but without recording stats. |
| [[nodiscard]] static rocksdb::Status ParseMetadata(RedisTypes types, Slice *bytes, Metadata *metadata); |
| /// GetMetadata is a helper function to get metadata from the database. It will read the "raw metadata" |
| /// from underlying storage, and then parse the raw metadata to the specified metadata type. |
| /// |
| /// \param options The read options, including whether uses a snapshot during reading the metadata. |
| /// \param types The candidate types of the metadata. |
| /// \param ns_key The key with namespace of the metadata. |
| /// \param metadata The output metadata. |
| [[nodiscard]] rocksdb::Status GetMetadata(engine::Context &ctx, RedisTypes types, const Slice &ns_key, |
| Metadata *metadata); |
| /// GetMetadata is a helper function to get metadata from the database. It will read the "raw metadata" |
| /// from underlying storage, and then parse the raw metadata to the specified metadata type. |
| /// |
| /// Compared with the above function, this function will also return the rest of the bytes |
| /// after parsing the metadata. |
| /// |
| /// \param options The read options, including whether uses a snapshot during reading the metadata. |
| /// \param types The candidate types of the metadata. |
| /// \param ns_key The key with namespace of the metadata. |
| /// \param raw_value Holding the raw metadata. |
| /// \param metadata The output metadata. |
| /// \param rest The rest of the bytes after parsing the metadata. |
| [[nodiscard]] rocksdb::Status GetMetadata(engine::Context &ctx, RedisTypes types, const Slice &ns_key, |
| std::string *raw_value, Metadata *metadata, Slice *rest); |
| /// GetRawMetadata is a helper function to get the "raw metadata" from the database without parsing |
| /// it to the specified metadata type. |
| /// |
| /// \param options The read options, including whether uses a snapshot during reading the metadata. |
| /// \param ns_key The key with namespace of the metadata. |
| /// \param bytes The output raw metadata. |
| [[nodiscard]] rocksdb::Status GetRawMetadata(engine::Context &ctx, const Slice &ns_key, std::string *bytes); |
| [[nodiscard]] rocksdb::Status Expire(engine::Context &ctx, const Slice &user_key, uint64_t timestamp); |
| [[nodiscard]] rocksdb::Status Del(engine::Context &ctx, const Slice &user_key); |
| [[nodiscard]] rocksdb::Status MDel(engine::Context &ctx, const std::vector<Slice> &keys, uint64_t *deleted_cnt); |
| [[nodiscard]] rocksdb::Status Exists(engine::Context &ctx, const std::vector<Slice> &keys, uint32_t *ret); |
| [[nodiscard]] rocksdb::Status TTL(engine::Context &ctx, const Slice &user_key, int64_t *ttl); |
| [[nodiscard]] rocksdb::Status GetExpireTime(engine::Context &ctx, const Slice &user_key, uint64_t *timestamp); |
| [[nodiscard]] rocksdb::Status Type(engine::Context &ctx, const Slice &key, RedisType *type); |
| [[nodiscard]] rocksdb::Status Dump(engine::Context &ctx, const Slice &user_key, std::vector<std::string> *infos); |
| [[nodiscard]] rocksdb::Status FlushDB(engine::Context &ctx); |
| [[nodiscard]] rocksdb::Status FlushAll(engine::Context &ctx); |
| [[nodiscard]] rocksdb::Status GetKeyNumStats(engine::Context &ctx, const std::string &prefix, KeyNumStats *stats); |
| [[nodiscard]] rocksdb::Status Keys(engine::Context &ctx, const std::string &prefix, const std::string &suffix_glob, |
| std::vector<std::string> *keys = nullptr, KeyNumStats *stats = nullptr); |
| [[nodiscard]] rocksdb::Status Scan(engine::Context &ctx, const std::string &cursor, uint64_t limit, |
| const std::string &prefix, const std::string &suffix_glob, |
| std::vector<std::string> *keys, std::string *end_cursor = nullptr, |
| RedisType type = kRedisNone, std::optional<int> scan_slot = std::nullopt); |
| [[nodiscard]] rocksdb::Status RandomKey(engine::Context &ctx, const std::string &cursor, std::string *key); |
| std::string AppendNamespacePrefix(const Slice &user_key); |
| [[nodiscard]] rocksdb::Status ClearKeysOfSlotRange(engine::Context &ctx, const rocksdb::Slice &ns, |
| const SlotRange &slot_range); |
| [[nodiscard]] rocksdb::Status KeyExist(engine::Context &ctx, const std::string &key); |
| |
| // Copy <key,value> to <new_key,value> (already an internal key) |
| enum class CopyResult { KEY_NOT_EXIST, KEY_ALREADY_EXIST, DONE }; |
| [[nodiscard]] rocksdb::Status Copy(engine::Context &ctx, const std::string &key, const std::string &new_key, bool nx, |
| bool delete_old, CopyResult *res); |
| enum class SortResult { UNKNOWN_TYPE, DOUBLE_CONVERT_ERROR, LIMIT_EXCEEDED, DONE }; |
| /// Sort sorts keys of the specified type according to SortArgument |
| /// |
| /// \param type is the type of sort key, which must be LIST, SET or ZSET |
| /// \param key is to be sorted |
| /// \param args provide the parameters to sort by |
| /// \param elems contain the sorted results |
| /// \param res represents the sorted result type. |
| /// When status is not ok, `res` should not been checked, otherwise it should be checked whether `res` is `DONE` |
| [[nodiscard]] rocksdb::Status Sort(engine::Context &ctx, RedisType type, const std::string &key, |
| const SortArgument &args, std::vector<std::optional<std::string>> *elems, |
| SortResult *res); |
| |
| protected: |
| engine::Storage *storage_; |
| rocksdb::ColumnFamilyHandle *metadata_cf_handle_; |
| std::string namespace_; |
| |
| private: |
| // Already internal keys |
| [[nodiscard]] rocksdb::Status existsInternal(engine::Context &ctx, const std::vector<std::string> &keys, |
| uint32_t *ret); |
| [[nodiscard]] rocksdb::Status typeInternal(engine::Context &ctx, const Slice &key, RedisType *type); |
| |
| /// lookupKeyByPattern is a helper function of `Sort` to support `GET` and `BY` fields. |
| /// |
| /// \param pattern can be the value of a `BY` or `GET` field |
| /// \param subst is used to replace the "*" or "#" matched in the pattern string. |
| /// \return Returns the value associated to the key with a name obtained using the following rules: |
| /// 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'. |
| /// 2) If 'pattern' matches the "->" string, everything on the left of |
| /// the arrow is treated as the name of a hash field, and the part on the |
| /// left as the key name containing a hash. The value of the specified |
| /// field is returned. |
| /// 3) If 'pattern' equals "#", the function simply returns 'subst' itself so |
| /// that the SORT command can be used like: SORT key GET # to retrieve |
| /// the Set/List elements directly. |
| std::optional<std::string> lookupKeyByPattern(engine::Context &ctx, const std::string &pattern, |
| const std::string &subst); |
| }; |
| |
| class SubKeyScanner : public redis::Database { |
| public: |
| explicit SubKeyScanner(engine::Storage *storage, const std::string &ns) : Database(storage, ns) {} |
| rocksdb::Status Scan(engine::Context &ctx, RedisType type, const Slice &user_key, const std::string &cursor, |
| uint64_t limit, const std::string &subkey_prefix, std::vector<std::string> *keys, |
| std::vector<std::string> *values = nullptr); |
| }; |
| |
| class WriteBatchLogData { |
| public: |
| WriteBatchLogData() = default; |
| explicit WriteBatchLogData(RedisType type) : type_(type) {} |
| explicit WriteBatchLogData(RedisType type, std::vector<std::string> &&args) : type_(type), args_(std::move(args)) {} |
| |
| RedisType GetRedisType() const; |
| std::vector<std::string> *GetArguments(); |
| std::string Encode() const; |
| Status Decode(const rocksdb::Slice &blob); |
| |
| private: |
| RedisType type_ = kRedisNone; |
| std::vector<std::string> args_; |
| }; |
| |
| } // namespace redis |