src/storage/redis_db.h (97 lines of code) (raw):

/* * 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, int *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); [[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, int *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