-
Notifications
You must be signed in to change notification settings - Fork 1.1k
chore: add active time to stream consumers #4285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,7 +46,7 @@ struct ParsedStreamId { | |
|
||
// Whether to lookup messages after the last ID in the stream. Used for XREAD | ||
// when using ID '$'. | ||
bool last_id = false; | ||
bool resolve_last_id = false; | ||
}; | ||
|
||
struct RangeId { | ||
|
@@ -82,7 +82,8 @@ struct NACKInfo { | |
|
||
struct ConsumerInfo { | ||
string name; | ||
size_t seen_time; | ||
uint64_t seen_time; | ||
int64_t active_time; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we have the same type for time? |
||
size_t pel_count; | ||
vector<NACKInfo> pending; | ||
size_t idle; | ||
|
@@ -769,6 +770,7 @@ OpResult<RecordVec> OpRange(const OpArgs& op_args, string_view key, const RangeO | |
LOG(DFATAL) << "Internal error"; | ||
return OpStatus::SKIPPED; // ("NACK half-created. Should not be possible."); | ||
} | ||
opts.consumer->active_time = now_ms; | ||
} | ||
if (opts.count == result.size()) | ||
break; | ||
|
@@ -985,6 +987,7 @@ void GetConsumers(stream* s, streamCG* cg, long long count, GroupInfo* ginfo) { | |
|
||
consumer_info.name = consumer->name; | ||
consumer_info.seen_time = consumer->seen_time; | ||
consumer_info.active_time = consumer->active_time; | ||
consumer_info.pel_count = raxSize(consumer->pel); | ||
|
||
/* Consumer PEL */ | ||
|
@@ -1106,6 +1109,7 @@ OpResult<vector<ConsumerInfo>> OpConsumers(const DbContext& db_cntx, EngineShard | |
consumer_info.name = consumer->name; | ||
consumer_info.pel_count = raxSize(consumer->pel); | ||
consumer_info.idle = idle; | ||
consumer_info.active_time = consumer->active_time; | ||
result.push_back(std::move(consumer_info)); | ||
} | ||
raxStop(&ri); | ||
|
@@ -1240,7 +1244,6 @@ OpResult<ClaimInfo> OpClaim(const OpArgs& op_args, string_view key, const ClaimO | |
auto cgr_res = FindGroup(op_args, key, opts.group); | ||
RETURN_ON_BAD_STATUS(cgr_res); | ||
|
||
streamConsumer* consumer = nullptr; | ||
uint64_t now_ms = op_args.db_cntx.time_now_ms; | ||
ClaimInfo result; | ||
result.justid = (opts.flags & kClaimJustID); | ||
|
@@ -1254,6 +1257,14 @@ OpResult<ClaimInfo> OpClaim(const OpArgs& op_args, string_view key, const ClaimO | |
|
||
StreamMemTracker tracker; | ||
|
||
// Try to get the consumer. If not found, create a new one. | ||
auto cname = WrapSds(opts.consumer); | ||
streamConsumer* consumer = streamLookupConsumer(cgr_res->cg, cname); | ||
if (consumer == nullptr) | ||
consumer = StreamCreateConsumer(cgr_res->cg, opts.consumer, now_ms, SCC_DEFAULT); | ||
else | ||
consumer->seen_time = now_ms; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I want to suggest making a separate function UpdateOrCreateConsumer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure |
||
|
||
for (streamID id : ids) { | ||
std::array<uint8_t, sizeof(streamID)> buf; | ||
StreamEncodeID(buf.begin(), &id); | ||
|
@@ -1288,13 +1299,6 @@ OpResult<ClaimInfo> OpClaim(const OpArgs& op_args, string_view key, const ClaimO | |
} | ||
} | ||
|
||
// Try to get the consumer. If not found, create a new one. | ||
auto cname = WrapSds(opts.consumer); | ||
if ((consumer = streamLookupConsumer(cgr_res->cg, cname, SLC_NO_REFRESH)) == nullptr) { | ||
consumer = StreamCreateConsumer(cgr_res->cg, opts.consumer, now_ms, | ||
SCC_NO_NOTIFY | SCC_NO_DIRTIFY); | ||
} | ||
|
||
// If the entry belongs to the same consumer, we don't have to | ||
// do anything. Else remove the entry from the old consumer. | ||
if (nack->consumer != consumer) { | ||
|
@@ -1320,9 +1324,11 @@ OpResult<ClaimInfo> OpClaim(const OpArgs& op_args, string_view key, const ClaimO | |
raxInsert(consumer->pel, buf.begin(), sizeof(buf), nack, nullptr); | ||
nack->consumer = consumer; | ||
} | ||
consumer->active_time = now_ms; | ||
|
||
/* Send the reply for this entry. */ | ||
AppendClaimResultItem(result, cgr_res->s, id); | ||
// TODO: propagate this change with streamPropagateXCLAIM | ||
} | ||
} | ||
tracker.UpdateStreamSize(cgr_res->it->second); | ||
|
@@ -1382,8 +1388,7 @@ OpResult<uint32_t> OpDelConsumer(const OpArgs& op_args, string_view key, string_ | |
StreamMemTracker mem_tracker; | ||
|
||
long long pending = 0; | ||
streamConsumer* consumer = | ||
streamLookupConsumer(cgroup_res->cg, WrapSds(consumer_name), SLC_NO_REFRESH); | ||
streamConsumer* consumer = streamLookupConsumer(cgroup_res->cg, WrapSds(consumer_name)); | ||
if (consumer) { | ||
pending = raxSize(consumer->pel); | ||
streamDelConsumer(cgroup_res->cg, consumer); | ||
|
@@ -1572,7 +1577,7 @@ OpResult<ClaimInfo> OpAutoClaim(const OpArgs& op_args, string_view key, const Cl | |
int count = opts.count; | ||
|
||
auto cname = WrapSds(opts.consumer); | ||
streamConsumer* consumer = streamLookupConsumer(group, cname, SLC_DEFAULT); | ||
streamConsumer* consumer = streamLookupConsumer(group, cname); | ||
if (consumer == nullptr) { | ||
consumer = StreamCreateConsumer(group, opts.consumer, now_ms, SCC_DEFAULT); | ||
// TODO: notify xgroup-createconsumer event once we support stream events. | ||
|
@@ -1621,7 +1626,7 @@ OpResult<ClaimInfo> OpAutoClaim(const OpArgs& op_args, string_view key, const Cl | |
raxInsert(consumer->pel, ri.key, ri.key_len, nack, nullptr); | ||
nack->consumer = consumer; | ||
} | ||
|
||
consumer->active_time = now_ms; | ||
AppendClaimResultItem(result, stream, id); | ||
count--; | ||
// TODO: propagate xclaim to replica | ||
|
@@ -1760,7 +1765,7 @@ OpResult<PendingResult> OpPending(const OpArgs& op_args, string_view key, const | |
|
||
streamConsumer* consumer = nullptr; | ||
if (!opts.consumer_name.empty()) { | ||
consumer = streamLookupConsumer(cgroup_res->cg, WrapSds(opts.consumer_name), SLC_NO_REFRESH); | ||
consumer = streamLookupConsumer(cgroup_res->cg, WrapSds(opts.consumer_name)); | ||
} | ||
|
||
PendingResult result; | ||
|
@@ -2153,7 +2158,7 @@ std::optional<ReadOpts> ParseReadArgsOrReply(CmdArgList args, bool read_group, | |
} | ||
id.val.ms = 0; | ||
id.val.seq = 0; | ||
id.last_id = true; | ||
id.resolve_last_id = true; | ||
sitem.id = id; | ||
auto [_, is_inserted] = opts.stream_ids.emplace(key, sitem); | ||
if (!is_inserted) { | ||
|
@@ -2330,7 +2335,7 @@ void XReadBlock(ReadOpts* opts, Transaction* tx, SinkReplyBuilder* builder, | |
// Update consumer | ||
if (sitem.group) { | ||
auto cname = WrapSds(opts->consumer_name); | ||
range_opts.consumer = streamLookupConsumer(sitem.group, cname, SLC_NO_REFRESH); | ||
range_opts.consumer = streamLookupConsumer(sitem.group, cname); | ||
if (!range_opts.consumer) { | ||
range_opts.consumer = StreamCreateConsumer( | ||
sitem.group, opts->consumer_name, GetCurrentTimeMs(), SCC_NO_NOTIFY | SCC_NO_DIRTIFY); | ||
|
@@ -2902,14 +2907,17 @@ void StreamFamily::XInfo(CmdArgList args, const CommandContext& cmd_cntx) { | |
rb->SendBulkString("consumers"); | ||
rb->StartArray(ginfo.consumer_info_vec.size()); | ||
for (const auto& consumer_info : ginfo.consumer_info_vec) { | ||
rb->StartCollection(4, RedisReplyBuilder::MAP); | ||
rb->StartCollection(5, RedisReplyBuilder::MAP); | ||
|
||
rb->SendBulkString("name"); | ||
rb->SendBulkString(consumer_info.name); | ||
|
||
rb->SendBulkString("seen-time"); | ||
rb->SendLong(consumer_info.seen_time); | ||
|
||
rb->SendBulkString("active-time"); | ||
rb->SendLong(consumer_info.active_time); | ||
|
||
rb->SendBulkString("pel-count"); | ||
rb->SendLong(consumer_info.pel_count); | ||
|
||
|
@@ -2961,14 +2969,20 @@ void StreamFamily::XInfo(CmdArgList args, const CommandContext& cmd_cntx) { | |
OpResult<vector<ConsumerInfo>> result = shard_set->Await(sid, std::move(cb)); | ||
if (result) { | ||
rb->StartArray(result->size()); | ||
int64_t now_ms = GetCurrentTimeMs(); | ||
for (const auto& consumer_info : *result) { | ||
rb->StartCollection(3, RedisReplyBuilder::MAP); | ||
int64_t active = consumer_info.active_time; | ||
int64_t inactive = active != -1 ? now_ms - active : -1; | ||
|
||
rb->StartCollection(4, RedisReplyBuilder::MAP); | ||
rb->SendBulkString("name"); | ||
rb->SendBulkString(consumer_info.name); | ||
rb->SendBulkString("pending"); | ||
rb->SendLong(consumer_info.pel_count); | ||
rb->SendBulkString("idle"); | ||
rb->SendLong(consumer_info.idle); | ||
rb->SendBulkString("inactive"); | ||
rb->SendLong(inactive); | ||
} | ||
return; | ||
} | ||
|
@@ -3099,26 +3113,17 @@ variant<bool, facade::ErrorReply> HasEntries2(const OpArgs& op_args, string_view | |
return facade::ErrorReply{ | ||
NoGroupOrKey(skey, opts->group_name, " in XREADGROUP with GROUP option")}; | ||
|
||
auto cname = WrapSds(opts->consumer_name); | ||
consumer = streamLookupConsumer(group, cname, SLC_NO_REFRESH); | ||
if (!consumer) { | ||
consumer = StreamCreateConsumer(group, opts->consumer_name, op_args.db_cntx.time_now_ms, | ||
SCC_NO_NOTIFY | SCC_NO_DIRTIFY); | ||
} | ||
sds cname = WrapSds(opts->consumer_name); | ||
consumer = streamLookupConsumer(group, cname); | ||
uint64_t now_ms = op_args.db_cntx.time_now_ms; | ||
if (consumer) | ||
consumer->seen_time = now_ms; | ||
else | ||
consumer = StreamCreateConsumer(group, opts->consumer_name, now_ms, SCC_DEFAULT); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you create a function UpdateOrCreateConsumer() you can use it here |
||
|
||
requested_sitem.group = group; | ||
requested_sitem.consumer = consumer; | ||
} | ||
|
||
// Resolve $ to the last ID in the stream. | ||
if (requested_sitem.id.last_id && !opts->read_group) { | ||
requested_sitem.id.val = last_id; | ||
streamIncrID(&requested_sitem.id.val); // include id's strictly greater | ||
requested_sitem.id.last_id = false; | ||
return false; | ||
} | ||
|
||
if (opts->read_group) { | ||
// If '>' is not provided, consumer PEL is used. So don't need to block. | ||
if (requested_sitem.id.val.ms != UINT64_MAX || requested_sitem.id.val.seq != UINT64_MAX) { | ||
opts->serve_history = true; | ||
|
@@ -3130,6 +3135,14 @@ variant<bool, facade::ErrorReply> HasEntries2(const OpArgs& op_args, string_view | |
requested_sitem.id.val = requested_sitem.group->last_id; | ||
streamIncrID(&requested_sitem.id.val); | ||
} | ||
} else { | ||
// Resolve $ to the last ID in the stream. | ||
if (requested_sitem.id.resolve_last_id) { | ||
requested_sitem.id.val = last_id; | ||
streamIncrID(&requested_sitem.id.val); // include id's strictly greater | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we check the result of streamIncrID(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I backported the changes from valkey. They do not check it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we check it sometimes and sometimes no. We can rewrite this method in this case and never check for example |
||
requested_sitem.id.resolve_last_id = false; | ||
return false; | ||
} | ||
} | ||
|
||
return streamCompareID(&last_id, &requested_sitem.id.val) >= 0; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks strange to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand the comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You write it in the description that we will use RDB_TYPE_STREAM_LISTPACKS_3 for FLAGS_stream_rdb_encode_v2,
but it looks strange to me