Skip to content

Commit 33762eb

Browse files
authored
Merge branch 'unstable' into unstable
2 parents a9f04a6 + 4450da2 commit 33762eb

14 files changed

Lines changed: 362 additions & 84 deletions

File tree

kvrocks.conf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,18 @@ replication-connect-timeout-ms 3100
193193
# future 'clusterx setnodes' commands because the replication thread is blocked on recv.
194194
replication-recv-timeout-ms 3200
195195

196+
# Maximum bytes to buffer before sending replication data to replicas.
197+
# The master will pack multiple write batches into one bulk to reduce network overhead,
198+
# but will send immediately if the bulk size exceeds this limit.
199+
# Default: 16KB (16384 bytes)
200+
replication-delay-bytes 16384
201+
202+
# Maximum number of updates to buffer before sending replication data to replicas.
203+
# The master will pack multiple write batches into one bulk to reduce network overhead,
204+
# but will send immediately if the number of updates exceeds this limit.
205+
# Default: 16 updates
206+
replication-delay-updates 16
207+
196208
# TCP listen() backlog.
197209
#
198210
# In high requests-per-second environments you need an high backlog in order

src/cluster/replication.cc

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@
5656
#include <openssl/ssl.h>
5757
#endif
5858

59+
FeedSlaveThread::FeedSlaveThread(Server *srv, redis::Connection *conn, rocksdb::SequenceNumber next_repl_seq)
60+
: srv_(srv),
61+
conn_(conn),
62+
next_repl_seq_(next_repl_seq),
63+
req_(srv),
64+
max_delay_bytes_(srv->GetConfig()->max_replication_delay_bytes),
65+
max_delay_updates_(srv->GetConfig()->max_replication_delay_updates) {}
66+
5967
Status FeedSlaveThread::Start() {
6068
auto s = util::CreateThread("feed-replica", [this] {
6169
sigset_t mask, omask;
@@ -154,6 +162,16 @@ void FeedSlaveThread::readCallback(bufferevent *bev, [[maybe_unused]] void *ctx)
154162
}
155163
}
156164

165+
bool FeedSlaveThread::shouldSendGetAck(rocksdb::SequenceNumber seq) {
166+
rocksdb::SequenceNumber largest_unblockable_seq = srv_->LargestTargetSeqToWakeup(seq);
167+
if (largest_unblockable_seq > last_getack_seq_) {
168+
last_getack_seq_ = largest_unblockable_seq;
169+
return true;
170+
}
171+
172+
return false;
173+
}
174+
157175
void FeedSlaveThread::loop() {
158176
// is_first_repl_batch was used to fix that replication may be stuck in a dead loop
159177
// when some seqs might be lost in the middle of the WAL log, so forced to replicate
@@ -194,18 +212,23 @@ void FeedSlaveThread::loop() {
194212
// 3. To avoid master don't send replication stream to slave since of packing
195213
// batches strategy, we still send batches if current batch sequence is less
196214
// kMaxDelayUpdates than latest sequence.
197-
if (is_first_repl_batch || batches_bulk.size() >= kMaxDelayBytes || updates_in_batches >= kMaxDelayUpdates ||
198-
srv_->storage->LatestSeqNumber() - batch.sequence <= kMaxDelayUpdates) {
215+
if (is_first_repl_batch || batches_bulk.size() >= max_delay_bytes_ || updates_in_batches >= max_delay_updates_ ||
216+
srv_->storage->LatestSeqNumber() - batch.sequence <= max_delay_updates_) {
217+
if (shouldSendGetAck(batch.sequence)) {
218+
batches_bulk += redis::BulkString("_getack");
219+
}
220+
199221
// Send entire bulk which contain multiple batches
200222
auto s = util::SockSend(conn_->GetFD(), batches_bulk, conn_->GetBufferEvent());
201223
if (!s.IsOK()) {
202224
error("Write error while sending batch to slave: {}. batches: 0x{}", s.Msg(), util::StringToHex(batches_bulk));
203225
Stop();
204226
return;
205227
}
228+
206229
is_first_repl_batch = false;
207230
batches_bulk.clear();
208-
if (batches_bulk.capacity() > kMaxDelayBytes * 2) batches_bulk.shrink_to_fit();
231+
if (batches_bulk.capacity() > max_delay_bytes_ * 2) batches_bulk.shrink_to_fit();
209232
updates_in_batches = 0;
210233
}
211234
curr_seq = batch.sequence + batch.writeBatchPtr->Count();
@@ -608,22 +631,29 @@ ReplicationThread::CBState ReplicationThread::tryPSyncReadCB(bufferevent *bev) {
608631
}
609632
}
610633

611-
void ReplicationThread::sendReplConfAck(bufferevent *bev) {
612-
SendString(bev, redis::ArrayOfBulkStrings({"replconf", "ack", std::to_string(storage_->LatestSeqNumber())}));
634+
void ReplicationThread::sendReplConfAck(bufferevent *bev, bool force) {
635+
int64_t now = util::GetTimeStamp();
636+
637+
// If force is true, always send ack. Otherwise, check if it has been 1s from last ack
638+
if (force || (now - last_ack_time_secs_) >= 1) {
639+
SendString(bev, redis::ArrayOfBulkStrings({"replconf", "ack", std::to_string(storage_->LatestSeqNumber())}));
640+
last_ack_time_secs_ = now;
641+
}
613642
}
614643

615644
ReplicationThread::CBState ReplicationThread::incrementBatchLoopCB(bufferevent *bev) {
616645
repl_state_.store(kReplConnected, std::memory_order_relaxed);
617646
auto input = bufferevent_get_input(bev);
618647
bool data_written = false;
648+
bool force_ack = false;
619649
while (true) {
620650
switch (incr_state_) {
621651
case Incr_batch_size: {
622652
// Read bulk length
623653
UniqueEvbufReadln line(input, EVBUFFER_EOL_CRLF_STRICT);
624654
if (!line) {
625655
if (data_written) {
626-
sendReplConfAck(bev);
656+
sendReplConfAck(bev, force_ack);
627657
}
628658
return CBState::AGAIN;
629659
}
@@ -639,7 +669,7 @@ ReplicationThread::CBState ReplicationThread::incrementBatchLoopCB(bufferevent *
639669
// Read bulk data (batch data)
640670
if (incr_bulk_len_ + 2 > evbuffer_get_length(input)) { // If data not enough
641671
if (data_written) {
642-
sendReplConfAck(bev);
672+
sendReplConfAck(bev, force_ack);
643673
}
644674
return CBState::AGAIN;
645675
}
@@ -654,11 +684,18 @@ ReplicationThread::CBState ReplicationThread::incrementBatchLoopCB(bufferevent *
654684
// master would send the ping heartbeat packet to check whether the slave was alive or not,
655685
// don't write ping to db here.
656686
if (data_written) {
657-
sendReplConfAck(bev);
687+
sendReplConfAck(bev, force_ack);
658688
}
659689
return CBState::AGAIN;
660690
}
661691

692+
if (bulk_string == "_getack") {
693+
// master would send the _getack command to the master to get acknowledgment
694+
// don't write _getack to db here.
695+
force_ack = true;
696+
continue;
697+
}
698+
662699
rocksdb::WriteBatch batch(std::move(bulk_string));
663700

664701
auto s = storage_->ReplicaApplyWriteBatch(&batch);

src/cluster/replication.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ using FetchFileCallback = std::function<void(const std::string &, uint32_t)>;
6464

6565
class FeedSlaveThread {
6666
public:
67-
explicit FeedSlaveThread(Server *srv, redis::Connection *conn, rocksdb::SequenceNumber next_repl_seq)
68-
: srv_(srv), conn_(conn), next_repl_seq_(next_repl_seq), req_(srv) {}
67+
explicit FeedSlaveThread(Server *srv, redis::Connection *conn, rocksdb::SequenceNumber next_repl_seq);
6968
~FeedSlaveThread() = default;
7069

7170
Status Start();
@@ -86,14 +85,18 @@ class FeedSlaveThread {
8685
// used to parse the ack response from the slave
8786
redis::Request req_;
8887
std::atomic<rocksdb::SequenceNumber> ack_seq_ = 0;
88+
rocksdb::SequenceNumber last_getack_seq_ = 0;
89+
int64_t last_ack_time_secs_ = 0;
8990

90-
static const size_t kMaxDelayUpdates = 16;
91-
static const size_t kMaxDelayBytes = 16 * 1024;
91+
// Configurable delay limits
92+
size_t max_delay_bytes_;
93+
size_t max_delay_updates_;
9294

9395
void loop();
9496
void checkLivenessIfNeed();
9597
void readCallback(bufferevent *bev, void *ctx);
9698
static void staticReadCallback(bufferevent *bev, void *ctx);
99+
bool shouldSendGetAck(rocksdb::SequenceNumber seq);
97100
};
98101

99102
class ReplicationThread : private EventCallbackBase<ReplicationThread> {
@@ -161,6 +164,7 @@ class ReplicationThread : private EventCallbackBase<ReplicationThread> {
161164
engine::Storage *storage_ = nullptr;
162165
std::atomic<ReplState> repl_state_;
163166
std::atomic<int64_t> last_io_time_secs_ = 0;
167+
int64_t last_ack_time_secs_ = 0;
164168
bool next_try_old_psync_ = false;
165169
bool next_try_without_announce_ip_address_ = false;
166170

@@ -202,8 +206,6 @@ class ReplicationThread : private EventCallbackBase<ReplicationThread> {
202206
CBState fullSyncWriteCB(bufferevent *bev);
203207
CBState fullSyncReadCB(bufferevent *bev);
204208

205-
void sendReplConfAck(bufferevent *bev);
206-
207209
Status sendAuth(int sock_fd, ssl_st *ssl);
208210
Status fetchFile(int sock_fd, evbuffer *evbuf, const std::string &dir, const std::string &file, uint32_t crc,
209211
const FetchFileCallback &fn, ssl_st *ssl);
@@ -214,6 +216,8 @@ class ReplicationThread : private EventCallbackBase<ReplicationThread> {
214216
static bool isWrongPsyncNum(std::string_view err);
215217
static bool isUnknownOption(std::string_view err);
216218

219+
void sendReplConfAck(bufferevent *bev, bool force = false);
220+
217221
Status parseWriteBatch(const rocksdb::WriteBatch &write_batch);
218222
};
219223

src/commands/cmd_server.cc

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,23 @@ class CommandNamespace : public Commander {
102102
Status s = srv->GetNamespace()->Del(args_[2]);
103103
*output = s.IsOK() ? redis::RESP_OK : redis::Error(s);
104104
warn("Deleted namespace: {}, addr: {}, result: {}", args_[2], conn->GetAddr(), s.Msg());
105+
} else if (args_.size() == 2 && sub_command == "current") {
106+
*output = redis::BulkString(conn->GetNamespace());
105107
} else {
106-
return {Status::RedisExecErr, "NAMESPACE subcommand must be one of GET, SET, DEL, ADD"};
108+
return {Status::RedisExecErr, "NAMESPACE subcommand must be one of GET, SET, DEL, ADD and CURRENT"};
107109
}
108110
return Status::OK();
109111
}
110112
};
111113

114+
static uint64_t GenerateNamespaceFlag(uint64_t flags, const std::vector<std::string> &args) {
115+
if (args.size() >= 2 && util::EqualICase(args[1], "current")) {
116+
return flags & ~kCmdAdmin;
117+
}
118+
119+
return flags;
120+
}
121+
112122
class CommandKeys : public Commander {
113123
public:
114124
Status Execute(engine::Context &ctx, Server *srv, Connection *conn, std::string *output) override {
@@ -1539,49 +1549,48 @@ class CommandFlushBlockCache : public Commander {
15391549
}
15401550
};
15411551

1542-
REDIS_REGISTER_COMMANDS(Server, MakeCmdAttr<CommandAuth>("auth", 2, "read-only ok-loading auth", NO_KEY),
1543-
MakeCmdAttr<CommandPing>("ping", -1, "read-only", NO_KEY),
1544-
MakeCmdAttr<CommandSelect>("select", 2, "read-only", NO_KEY),
1545-
MakeCmdAttr<CommandInfo>("info", -1, "read-only ok-loading", NO_KEY),
1546-
MakeCmdAttr<CommandRole>("role", 1, "read-only ok-loading", NO_KEY),
1547-
MakeCmdAttr<CommandConfig>("config", -2, "read-only admin", NO_KEY, GenerateConfigFlag),
1548-
MakeCmdAttr<CommandNamespace>("namespace", -3, "read-only admin", NO_KEY),
1549-
MakeCmdAttr<CommandKeys>("keys", 2, "read-only slow", NO_KEY),
1550-
MakeCmdAttr<CommandFlushDB>("flushdb", 1, "write no-dbsize-check exclusive", NO_KEY),
1551-
MakeCmdAttr<CommandFlushAll>("flushall", 1, "write no-dbsize-check exclusive admin", NO_KEY),
1552-
MakeCmdAttr<CommandDBSize>("dbsize", -1, "read-only", NO_KEY),
1553-
MakeCmdAttr<CommandSlowlog>("slowlog", -2, "read-only", NO_KEY),
1554-
MakeCmdAttr<CommandKProfile>("kprofile", -3, "read-only admin", NO_KEY),
1555-
MakeCmdAttr<CommandPerfLog>("perflog", -2, "read-only", NO_KEY),
1556-
MakeCmdAttr<CommandClient>("client", -2, "read-only", NO_KEY),
1557-
MakeCmdAttr<CommandMonitor>("monitor", 1, "read-only no-multi no-script", NO_KEY),
1558-
MakeCmdAttr<CommandShutdown>("shutdown", 1, "read-only exclusive no-multi no-script admin",
1559-
NO_KEY),
1560-
MakeCmdAttr<CommandQuit>("quit", 1, "read-only", NO_KEY),
1561-
MakeCmdAttr<CommandScan>("scan", -2, "read-only", NO_KEY),
1562-
MakeCmdAttr<CommandRandomKey>("randomkey", 1, "read-only", NO_KEY),
1563-
MakeCmdAttr<CommandDebug>("debug", -2, "read-only exclusive", NO_KEY, CommandDebug::FlagGen),
1564-
MakeCmdAttr<CommandCommand>("command", -1, "read-only", NO_KEY),
1565-
MakeCmdAttr<CommandEcho>("echo", 2, "read-only", NO_KEY),
1566-
MakeCmdAttr<CommandTime>("time", 1, "read-only ok-loading", NO_KEY),
1567-
MakeCmdAttr<CommandDisk>("disk", 3, "read-only", 2, 2, 1),
1568-
MakeCmdAttr<CommandMemory>("memory", 3, "read-only", 2, 2, 1),
1569-
MakeCmdAttr<CommandHello>("hello", -1, "read-only ok-loading auth", NO_KEY),
1570-
MakeCmdAttr<CommandRestore>("restore", -4, "write", 1, 1, 1),
1571-
1572-
MakeCmdAttr<CommandCompact>("compact", 1, "read-only no-script", NO_KEY),
1573-
MakeCmdAttr<CommandBGSave>("bgsave", 1, "read-only no-script admin", NO_KEY),
1574-
MakeCmdAttr<CommandLastSave>("lastsave", -1, "read-only admin", NO_KEY),
1575-
MakeCmdAttr<CommandFlushBackup>("flushbackup", 1, "read-only no-script admin", NO_KEY),
1576-
MakeCmdAttr<CommandSlaveOf>("slaveof", 3, "read-only exclusive no-script admin", NO_KEY),
1577-
MakeCmdAttr<CommandSlaveOf>("replicaof", 3, "read-only exclusive no-script admin", NO_KEY),
1578-
MakeCmdAttr<CommandStats>("stats", 1, "read-only", NO_KEY),
1579-
MakeCmdAttr<CommandRdb>("rdb", -3, "write exclusive admin", NO_KEY),
1580-
MakeCmdAttr<CommandReset>("reset", 1, "ok-loading bypass-multi no-script", NO_KEY),
1581-
MakeCmdAttr<CommandApplyBatch>("applybatch", -2, "write no-multi", NO_KEY),
1582-
MakeCmdAttr<CommandDump>("dump", 2, "read-only", 1, 1, 1),
1583-
MakeCmdAttr<CommandPollUpdates>("pollupdates", -2, "read-only admin", NO_KEY),
1584-
MakeCmdAttr<CommandSST>("sst", -3, "write exclusive admin", 1, 1, 1),
1585-
MakeCmdAttr<CommandFlushMemTable>("flushmemtable", -1, "exclusive write", NO_KEY),
1586-
MakeCmdAttr<CommandFlushBlockCache>("flushblockcache", 1, "exclusive write", NO_KEY), )
1552+
REDIS_REGISTER_COMMANDS(
1553+
Server, MakeCmdAttr<CommandAuth>("auth", 2, "read-only ok-loading auth", NO_KEY),
1554+
MakeCmdAttr<CommandPing>("ping", -1, "read-only", NO_KEY),
1555+
MakeCmdAttr<CommandSelect>("select", 2, "read-only", NO_KEY),
1556+
MakeCmdAttr<CommandInfo>("info", -1, "read-only ok-loading", NO_KEY),
1557+
MakeCmdAttr<CommandRole>("role", 1, "read-only ok-loading", NO_KEY),
1558+
MakeCmdAttr<CommandConfig>("config", -2, "read-only admin", NO_KEY, GenerateConfigFlag),
1559+
MakeCmdAttr<CommandNamespace>("namespace", -2, "read-only admin", NO_KEY, GenerateNamespaceFlag),
1560+
MakeCmdAttr<CommandKeys>("keys", 2, "read-only slow", NO_KEY),
1561+
MakeCmdAttr<CommandFlushDB>("flushdb", 1, "write no-dbsize-check exclusive", NO_KEY),
1562+
MakeCmdAttr<CommandFlushAll>("flushall", 1, "write no-dbsize-check exclusive admin", NO_KEY),
1563+
MakeCmdAttr<CommandDBSize>("dbsize", -1, "read-only", NO_KEY),
1564+
MakeCmdAttr<CommandSlowlog>("slowlog", -2, "read-only", NO_KEY),
1565+
MakeCmdAttr<CommandKProfile>("kprofile", -3, "read-only admin", NO_KEY),
1566+
MakeCmdAttr<CommandPerfLog>("perflog", -2, "read-only", NO_KEY),
1567+
MakeCmdAttr<CommandClient>("client", -2, "read-only", NO_KEY),
1568+
MakeCmdAttr<CommandMonitor>("monitor", 1, "read-only no-multi no-script", NO_KEY),
1569+
MakeCmdAttr<CommandShutdown>("shutdown", 1, "read-only exclusive no-multi no-script admin", NO_KEY),
1570+
MakeCmdAttr<CommandQuit>("quit", 1, "read-only", NO_KEY), MakeCmdAttr<CommandScan>("scan", -2, "read-only", NO_KEY),
1571+
MakeCmdAttr<CommandRandomKey>("randomkey", 1, "read-only", NO_KEY),
1572+
MakeCmdAttr<CommandDebug>("debug", -2, "read-only exclusive", NO_KEY, CommandDebug::FlagGen),
1573+
MakeCmdAttr<CommandCommand>("command", -1, "read-only", NO_KEY),
1574+
MakeCmdAttr<CommandEcho>("echo", 2, "read-only", NO_KEY),
1575+
MakeCmdAttr<CommandTime>("time", 1, "read-only ok-loading", NO_KEY),
1576+
MakeCmdAttr<CommandDisk>("disk", 3, "read-only", 2, 2, 1),
1577+
MakeCmdAttr<CommandMemory>("memory", 3, "read-only", 2, 2, 1),
1578+
MakeCmdAttr<CommandHello>("hello", -1, "read-only ok-loading auth", NO_KEY),
1579+
MakeCmdAttr<CommandRestore>("restore", -4, "write", 1, 1, 1),
1580+
1581+
MakeCmdAttr<CommandCompact>("compact", 1, "read-only no-script", NO_KEY),
1582+
MakeCmdAttr<CommandBGSave>("bgsave", 1, "read-only no-script admin", NO_KEY),
1583+
MakeCmdAttr<CommandLastSave>("lastsave", -1, "read-only admin", NO_KEY),
1584+
MakeCmdAttr<CommandFlushBackup>("flushbackup", 1, "read-only no-script admin", NO_KEY),
1585+
MakeCmdAttr<CommandSlaveOf>("slaveof", 3, "read-only exclusive no-script admin", NO_KEY),
1586+
MakeCmdAttr<CommandSlaveOf>("replicaof", 3, "read-only exclusive no-script admin", NO_KEY),
1587+
MakeCmdAttr<CommandStats>("stats", 1, "read-only", NO_KEY),
1588+
MakeCmdAttr<CommandRdb>("rdb", -3, "write exclusive admin", NO_KEY),
1589+
MakeCmdAttr<CommandReset>("reset", 1, "ok-loading bypass-multi no-script", NO_KEY),
1590+
MakeCmdAttr<CommandApplyBatch>("applybatch", -2, "write no-multi", NO_KEY),
1591+
MakeCmdAttr<CommandDump>("dump", 2, "read-only", 1, 1, 1),
1592+
MakeCmdAttr<CommandPollUpdates>("pollupdates", -2, "read-only admin", NO_KEY),
1593+
MakeCmdAttr<CommandSST>("sst", -3, "write exclusive admin", 1, 1, 1),
1594+
MakeCmdAttr<CommandFlushMemTable>("flushmemtable", -1, "exclusive write", NO_KEY),
1595+
MakeCmdAttr<CommandFlushBlockCache>("flushblockcache", 1, "exclusive write", NO_KEY), )
15871596
} // namespace redis

src/config/config.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ Config::Config() {
203203
{"slave-read-only", false, new YesNoField(&slave_readonly, true)},
204204
{"replication-connect-timeout-ms", false, new IntField(&replication_connect_timeout_ms, 3100, 0, INT_MAX)},
205205
{"replication-recv-timeout-ms", false, new IntField(&replication_recv_timeout_ms, 3200, 0, INT_MAX)},
206+
{"replication-delay-bytes", false, new IntField(&max_replication_delay_bytes, 16 * 1024, 1, INT_MAX)},
207+
{"replication-delay-updates", false, new IntField(&max_replication_delay_updates, 16, 1, INT_MAX)},
206208
{"use-rsid-psync", true, new YesNoField(&use_rsid_psync, false)},
207209
{"profiling-sample-ratio", false, new IntField(&profiling_sample_ratio, 0, 0, 100)},
208210
{"profiling-sample-record-max-len", false, new IntField(&profiling_sample_record_max_len, 256, 0, INT_MAX)},

src/config/config.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ struct Config {
120120
int slave_priority = 100;
121121
int replication_connect_timeout_ms = 3100;
122122
int replication_recv_timeout_ms = 3200;
123+
int max_replication_delay_bytes = 16 * 1024; // 16KB default
124+
int max_replication_delay_updates = 16; // 16 updates default
123125
int max_db_size = 0;
124126
int max_replication_mb = 0;
125127
int max_io_mb = 0;

src/search/executors/filter_executor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ struct QueryExprEvaluator {
7878

7979
StatusOr<bool> Visit(TagContainExpr *v) const {
8080
auto val = GET_OR_RET(ctx->Retrieve(ctx->db_ctx, row, v->field->info));
81+
if (val.IsNull()) return false;
8182

8283
CHECK(val.Is<kqir::StringArray>());
8384
auto tags = val.Get<kqir::StringArray>();
@@ -93,6 +94,7 @@ struct QueryExprEvaluator {
9394

9495
StatusOr<bool> Visit(NumericCompareExpr *v) const {
9596
auto l_val = GET_OR_RET(ctx->Retrieve(ctx->db_ctx, row, v->field->info));
97+
if (l_val.IsNull()) return false;
9698

9799
CHECK(l_val.Is<kqir::Numeric>());
98100
auto l = l_val.Get<kqir::Numeric>();
@@ -118,6 +120,7 @@ struct QueryExprEvaluator {
118120

119121
StatusOr<bool> Visit(VectorRangeExpr *v) const {
120122
auto val = GET_OR_RET(ctx->Retrieve(ctx->db_ctx, row, v->field->info));
123+
if (val.IsNull()) return false;
121124

122125
CHECK(val.Is<kqir::NumericArray>());
123126
auto l_values = val.Get<kqir::NumericArray>();

src/search/executors/topn_executor.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ struct TopNExecutor : ExecutorNode {
5858

5959
auto get_order = [this](RowType &row) -> StatusOr<double> {
6060
auto order_val = GET_OR_RET(ctx->Retrieve(ctx->db_ctx, row, topn->order->field->info));
61+
// TODO(twice): here we return NaN if this field is not found,
62+
// but we should consider to just skip this row instead.
63+
if (order_val.IsNull()) return std::nan("");
6164
CHECK(order_val.Is<kqir::Numeric>());
6265
return order_val.Get<kqir::Numeric>();
6366
};

0 commit comments

Comments
 (0)