blob: e360ffe0c2d940adba520c6060e066a56e2f6e8b [file]
/*
* 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.
*
*/
#include "iterator.h"
#include <cluster/redis_slot.h>
#include "db_util.h"
namespace engine {
DBIterator::DBIterator(engine::Context &ctx, rocksdb::ReadOptions read_options, int slot)
: storage_(ctx.storage),
read_options_(std::move(read_options)),
ctx_(&ctx),
slot_(slot),
metadata_cf_handle_(storage_->GetCFHandle(ColumnFamilyID::Metadata)) {
metadata_iter_ = util::UniqueIterator(storage_->NewIterator(ctx, read_options_, metadata_cf_handle_));
}
void DBIterator::Next() {
if (!Valid()) return;
metadata_iter_->Next();
nextUntilValid();
}
void DBIterator::nextUntilValid() {
// slot_ != -1 means we would like to iterate all keys in the slot
// so we can skip the afterwards keys if the slot id doesn't match
if (slot_ != -1 && metadata_iter_->Valid()) {
auto [_, user_key] = ExtractNamespaceKey(metadata_iter_->key(), storage_->IsSlotIdEncoded());
// Release the iterator if the slot id doesn't match
if (GetSlotIdFromKey(user_key.ToString()) != slot_) {
Reset();
return;
}
}
while (metadata_iter_->Valid()) {
Metadata metadata(kRedisNone, false);
// Skip the metadata if it's expired
if (metadata.Decode(metadata_iter_->value()).ok() && !metadata.Expired()) {
metadata_ = metadata;
break;
}
metadata_iter_->Next();
}
}
bool DBIterator::Valid() const { return metadata_iter_ && metadata_iter_->Valid(); }
Slice DBIterator::Key() const { return Valid() ? metadata_iter_->key() : Slice(); }
std::tuple<Slice, Slice> DBIterator::UserKey() const {
if (!Valid()) {
return {};
}
return ExtractNamespaceKey(metadata_iter_->key(), slot_ != -1);
}
Slice DBIterator::Value() const { return Valid() ? metadata_iter_->value() : Slice(); }
RedisType DBIterator::Type() const { return Valid() ? metadata_.Type() : kRedisNone; }
void DBIterator::Reset() {
if (metadata_iter_) metadata_iter_.reset();
}
void DBIterator::Seek(const std::string &target) {
if (!metadata_iter_) return;
// Iterate with the slot id but storage didn't enable slot id encoding
if (slot_ != -1 && !storage_->IsSlotIdEncoded()) {
Reset();
return;
}
std::string prefix = target;
if (slot_ != -1) {
// Use the slot id as the prefix if it's specified
prefix = ComposeSlotKeyPrefix(kDefaultNamespace, slot_) + target;
}
metadata_iter_->Seek(prefix);
nextUntilValid();
}
std::unique_ptr<SubKeyIterator> DBIterator::GetSubKeyIterator() const {
if (!Valid()) {
return nullptr;
}
RedisType type = metadata_.Type();
if (type == kRedisNone || metadata_.IsSingleKVType()) {
return nullptr;
}
auto prefix = InternalKey(Key(), "", metadata_.version, ctx_->storage->IsSlotIdEncoded()).Encode();
return std::make_unique<SubKeyIterator>(*ctx_, read_options_, type, std::move(prefix));
}
SubKeyIterator::SubKeyIterator(engine::Context &ctx, rocksdb::ReadOptions read_options, RedisType type,
std::string prefix)
: storage_(ctx.storage), read_options_(std::move(read_options)), type_(type), prefix_(std::move(prefix)) {
if (type_ == kRedisStream) {
cf_handle_ = storage_->GetCFHandle(ColumnFamilyID::Stream);
} else {
cf_handle_ = storage_->GetCFHandle(ColumnFamilyID::PrimarySubkey);
}
iter_ = util::UniqueIterator(storage_->NewIterator(ctx, read_options_, cf_handle_));
}
void SubKeyIterator::Next() {
if (!Valid()) return;
iter_->Next();
if (!Valid()) return;
if (!iter_->key().starts_with(prefix_)) {
Reset();
}
}
bool SubKeyIterator::Valid() const { return iter_ && iter_->Valid(); }
Slice SubKeyIterator::Key() const { return Valid() ? iter_->key() : Slice(); }
Slice SubKeyIterator::UserKey() const {
if (!Valid()) return {};
const InternalKey internal_key(iter_->key(), storage_->IsSlotIdEncoded());
return internal_key.GetSubKey();
}
rocksdb::ColumnFamilyHandle *SubKeyIterator::ColumnFamilyHandle() const { return Valid() ? this->cf_handle_ : nullptr; }
Slice SubKeyIterator::Value() const { return Valid() ? iter_->value() : Slice(); }
void SubKeyIterator::Seek() {
if (!iter_) return;
iter_->Seek(prefix_);
if (!iter_->Valid()) return;
// For the subkey iterator, it MUST contain the prefix key itself
if (!iter_->key().starts_with(prefix_)) {
Reset();
}
}
void SubKeyIterator::Reset() {
if (iter_) iter_.reset();
}
rocksdb::Status WALBatchExtractor::PutCF(uint32_t column_family_id, const Slice &key, const Slice &value) {
auto key_slot_id = ExtractSlotId(key);
if (slot_range_.IsValid() && !slot_range_.Contains(key_slot_id)) {
return rocksdb::Status::OK();
}
items_.emplace_back(WALItem::Type::kTypePut, column_family_id, key.ToString(), value.ToString());
return rocksdb::Status::OK();
}
rocksdb::Status WALBatchExtractor::DeleteCF(uint32_t column_family_id, const rocksdb::Slice &key) {
auto key_slot_id = ExtractSlotId(key);
if (slot_range_.IsValid() && !slot_range_.Contains(key_slot_id)) {
return rocksdb::Status::OK();
}
items_.emplace_back(WALItem::Type::kTypeDelete, column_family_id, key.ToString(), std::string{});
return rocksdb::Status::OK();
}
rocksdb::Status WALBatchExtractor::DeleteRangeCF(uint32_t column_family_id, const rocksdb::Slice &begin_key,
const rocksdb::Slice &end_key) {
items_.emplace_back(WALItem::Type::kTypeDeleteRange, column_family_id, begin_key.ToString(), end_key.ToString());
return rocksdb::Status::OK();
}
void WALBatchExtractor::LogData(const rocksdb::Slice &blob) {
items_.emplace_back(WALItem::Type::kTypeLogData, 0, blob.ToString(), std::string{});
};
void WALBatchExtractor::Clear() { items_.clear(); }
WALBatchExtractor::Iter WALBatchExtractor::GetIter() { return Iter(&items_); }
bool WALBatchExtractor::Iter::Valid() { return items_ && cur_ < items_->size(); }
void WALBatchExtractor::Iter::Next() { cur_++; }
WALItem WALBatchExtractor::Iter::Value() {
if (!Valid()) {
return {};
}
return (*items_)[cur_];
}
void WALIterator::Reset() {
if (iter_) {
iter_.reset();
}
if (batch_iter_) {
batch_iter_.reset();
}
extractor_.Clear();
next_batch_seq_ = 0;
}
bool WALIterator::Valid() const { return (batch_iter_ && batch_iter_->Valid()) || (iter_ && iter_->Valid()); }
void WALIterator::nextBatch() {
if (!iter_ || !iter_->Valid()) {
Reset();
return;
}
auto batch = iter_->GetBatch();
if (batch.sequence != next_batch_seq_ || !batch.writeBatchPtr) {
Reset();
return;
}
extractor_.Clear();
auto s = batch.writeBatchPtr->Iterate(&extractor_);
if (!s.ok()) {
Reset();
return;
}
next_batch_seq_ += batch.writeBatchPtr->Count();
batch_iter_ = std::make_unique<WALBatchExtractor::Iter>(extractor_.GetIter());
}
void WALIterator::Seek(rocksdb::SequenceNumber seq) {
if (slot_range_.IsValid() && !storage_->IsSlotIdEncoded()) {
Reset();
return;
}
auto s = storage_->GetWALIter(seq, &iter_);
if (!s.IsOK()) {
Reset();
return;
}
next_batch_seq_ = seq;
nextBatch();
}
WALItem WALIterator::Item() {
if (batch_iter_ && batch_iter_->Valid()) {
return batch_iter_->Value();
}
return {};
}
rocksdb::SequenceNumber WALIterator::NextSequenceNumber() const { return next_batch_seq_; }
void WALIterator::Next() {
if (!Valid()) {
Reset();
return;
}
if (batch_iter_ && batch_iter_->Valid()) {
batch_iter_->Next();
if (batch_iter_->Valid()) {
return;
}
}
iter_->Next();
nextBatch();
}
} // namespace engine