fix(console): bound the pending queue by lines and bytes
The console's pending, not-yet-drained queue was unbounded, so a hidden Console page that kept receiving verbose daemon-log chunks could grow without limit. - Pending lines are now bounded by both a line count and a byte budget. The oldest pending lines are dropped once either is exceeded, while the newest line is always kept so one huge line is never lost. - A formatted RPC result is ingested as one block under a single lock, so lines from other producers can never interleave inside it. The front-sequence accounting is also corrected: a pending line dropped by the cap never entered the visible buffer, so it no longer advances the front sequence. Only a front eviction or a clear does. Tests: the line and byte caps keep the newest lines, a single oversized line survives, a batch appends contiguously, and the front sequence advances only on a front eviction or clear. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+31
-7
@@ -7313,19 +7313,22 @@ void testConsoleWrapCache()
|
||||
EXPECT_EQ(c2.size(), static_cast<std::size_t>(0));
|
||||
}
|
||||
|
||||
// 7) ConsoleModel::firstSeq: +1 per evicted line, +size() on clear(), untouched by fold toggles.
|
||||
// 7) ConsoleModel::firstSeq advances only when a line leaves the FRONT of lines_ (cap eviction or
|
||||
// clear); a pending line dropped by the byte/line budget never entered lines_ and does not advance
|
||||
// it. Untouched by fold toggles. (cap 3: 0..4 ingested -> pending drops "0","1" before they reach
|
||||
// lines_, so lines_=[2,3,4] with zero front evictions -> firstSeq 0.)
|
||||
{
|
||||
ConsoleModel cm(3);
|
||||
for (int i = 0; i < 5; ++i) cm.ingest(std::to_string(i), ConsoleChannel::None);
|
||||
cm.drain();
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(2));
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(0)); // "2","3","4" land; nothing evicted from front
|
||||
cm.ingest("{", ConsoleChannel::JsonBrace, 2);
|
||||
cm.drain();
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(3));
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(1)); // "{" pushes over cap -> evict "2" from front
|
||||
cm.toggleCollapsed(2);
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(3));
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(1));
|
||||
cm.clear();
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(6));
|
||||
EXPECT_EQ(cm.firstSeq(), static_cast<std::uint64_t>(4)); // + lines_.size() (3) on clear
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7536,13 +7539,34 @@ void testConsoleModel()
|
||||
ConsoleModel m(3); // cap = 3
|
||||
for (int i = 0; i < 5; ++i) m.ingest(std::to_string(i), ConsoleChannel::None);
|
||||
auto dr = m.drain();
|
||||
EXPECT_EQ(dr.added, static_cast<size_t>(5));
|
||||
EXPECT_EQ(dr.popped, static_cast<size_t>(2));
|
||||
EXPECT_EQ(dr.added, static_cast<size_t>(3)); // pending is capped at ingest: "0","1" never reach the deque
|
||||
EXPECT_EQ(dr.popped, static_cast<size_t>(0));
|
||||
EXPECT_EQ(dr.dropped, static_cast<size_t>(2));
|
||||
EXPECT_EQ(m.size(), static_cast<size_t>(3));
|
||||
EXPECT_EQ(m[0].text, std::string("2")); // "0" and "1" evicted
|
||||
EXPECT_EQ(m[2].text, std::string("4"));
|
||||
}
|
||||
|
||||
// Pending byte budget keeps the newest lines; a single oversized line is never dropped.
|
||||
{
|
||||
ConsoleModel m(100, 10);
|
||||
for (int i = 0; i < 5; ++i) m.ingest("abcd", ConsoleChannel::None);
|
||||
auto dr = m.drain();
|
||||
EXPECT_EQ(dr.added, static_cast<size_t>(2));
|
||||
EXPECT_EQ(dr.dropped, static_cast<size_t>(3));
|
||||
ConsoleModel big(100, 10);
|
||||
big.ingest(std::string(50, 'x'), ConsoleChannel::None);
|
||||
EXPECT_EQ(big.drain().added, static_cast<size_t>(1));
|
||||
// ingestBatch appends a block contiguously.
|
||||
std::vector<dragonx::ui::ConsoleModelLine> blk(3);
|
||||
blk[0].text = "B0"; blk[1].text = "B1"; blk[2].text = "B2";
|
||||
ConsoleModel bm;
|
||||
bm.ingestBatch(std::move(blk));
|
||||
bm.drain();
|
||||
EXPECT_EQ(bm.size(), static_cast<size_t>(3));
|
||||
EXPECT_EQ(bm[2].text, std::string("B2"));
|
||||
}
|
||||
|
||||
// clear() drops the visible lines but not lines still queued for the next drain.
|
||||
{
|
||||
ConsoleModel m;
|
||||
|
||||
Reference in New Issue
Block a user