diff --git a/src/storage/shard.cpp b/src/storage/shard.cpp index 02296430..75d1444e 100644 --- a/src/storage/shard.cpp +++ b/src/storage/shard.cpp @@ -641,6 +641,13 @@ bool Shard::ProcessReq(KvRequest *req) auto lbd = [task, req]() -> KvError { auto floor_req = static_cast(req); + // Floor's large_value_ is an IoStringBuffer; in pinned mode its + // fragments come from the unrecyclable shard-private GC pool, same + // as the Read IoStringBuffer arm rejected above. + if (!shard->store_->Options().pinned_memory_chunks.empty()) + { + return KvError::InvalidArgs; + } KvError err = task->Floor(req->TableId(), floor_req->Key(), floor_req->floor_key_, diff --git a/tests/large_value_e2e.cpp b/tests/large_value_e2e.cpp index 3a404d85..71bc98f9 100644 --- a/tests/large_value_e2e.cpp +++ b/tests/large_value_e2e.cpp @@ -714,6 +714,28 @@ TEST_CASE("EloqStore pinned write + read round-trips with metadata", CleanupStore(opts); } +// Floor has only an IoStringBuffer destination, which draws from the +// unrecyclable shard-private GC pool in pinned mode, so it is rejected. +TEST_CASE("EloqStore rejects Floor in pinned mode", "[large-value-e2e][pinned]") +{ + PinnedHarness harness; + auto opts = MakePinnedOpts(harness); + auto *store = InitStore(opts); + + eloqstore::TableIdent tbl{"pinned-floor", 0}; + const size_t value_size = harness.SegmentSize() * 3; + auto write_buf = harness.AllocateSegmentAligned(value_size); + WritePinned(store, harness, tbl, "k", write_buf, 0xf0u); + + eloqstore::FloorRequest r; + r.SetArgs(tbl, "k"); + store->ExecSync(&r); + REQUIRE(r.Error() == eloqstore::KvError::InvalidArgs); + + store->Stop(); + CleanupStore(opts); +} + TEST_CASE("EloqStore mixed metadata / no-metadata large values via overload A", "[large-value-e2e][pinned]") {