Skip to content

Commit 930b493

Browse files
juncgu-googlecopybara-github
authored andcommitted
Filter caller RaidenId and prune invalid entries in Global Registry Lookup in TPU Raiden.
PiperOrigin-RevId: 971683436
1 parent cec884f commit 930b493

6 files changed

Lines changed: 124 additions & 17 deletions

File tree

tpu_sync/kv_cache/global_registry/global_registry.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ message LookupRequest {
151151
// first miss.
152152
// `bytes` for the reason given on RegisterEntry.prefix_hash.
153153
repeated bytes prefix_hashes = 1;
154+
155+
// Optional. If set, the server skips this caller and offers other holders
156+
// for the same hash instead of returning the caller to itself.
157+
tpu_sync.rpc.RaidenIdProto client_raiden_id = 2;
154158
}
155159

156160
message LookupResponse {

tpu_sync/kv_cache/global_registry/global_registry_client.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,16 @@ absl::Status GlobalRegistryClient::Register(
132132
}
133133

134134
absl::StatusOr<std::vector<KVBlockMetadata>> GlobalRegistryClient::Lookup(
135-
const std::vector<std::string>& prefix_hashes) {
135+
const std::vector<std::string>& prefix_hashes,
136+
const RaidenId& client_raiden_id) {
136137
LookupRequest request;
137138
request.mutable_prefix_hashes()->Reserve(prefix_hashes.size());
138139
for (const auto& hash : prefix_hashes) {
139140
request.add_prefix_hashes(hash);
140141
}
142+
if (!client_raiden_id.empty()) {
143+
ToProto(client_raiden_id, request.mutable_client_raiden_id());
144+
}
141145

142146
LookupResponse response;
143147
grpc::ClientContext context;

tpu_sync/kv_cache/global_registry/global_registry_client.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ class GlobalRegistryClient {
8181
// (a hash with no active registrations). All subsequent prefix hashes in the
8282
// input vector are treated as misses and are omitted from the response.
8383
// The returned vector is aligned in order with the input `prefix_hashes` (the
84-
// i-th element of the returned vector corresponds to the i-th input hash).
85-
// The size of the returned vector will be equal to the number of sequential
86-
// hits before the first miss.
84+
// If `client_raiden_id` is set, the server excludes it when returning
85+
// holders.
8786
absl::StatusOr<std::vector<KVBlockMetadata>> Lookup(
88-
const std::vector<std::string>& prefix_hashes);
87+
const std::vector<std::string>& prefix_hashes,
88+
const RaidenId& client_raiden_id = {});
8989

9090
// Unregisters a batch of KV cache entries for a raiden id asynchronously.
9191
// See RegisterAsync for what the future resolves to, and for the meaning of

tpu_sync/kv_cache/global_registry/global_registry_server.cc

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ grpc::Status GlobalRegistryServiceImpl::Register(grpc::ServerContext* context,
163163
grpc::Status GlobalRegistryServiceImpl::Lookup(grpc::ServerContext* context,
164164
const LookupRequest* request,
165165
LookupResponse* response) {
166+
const RaidenId caller = FromProto(request->client_raiden_id());
167+
const bool filter_caller = !caller.empty();
168+
166169
absl::MutexLock lock(mutex_);
167170
absl::Time now = absl::Now();
168171

@@ -172,23 +175,31 @@ grpc::Status GlobalRegistryServiceImpl::Lookup(grpc::ServerContext* context,
172175
break;
173176
}
174177

175-
const auto& entries = it->second;
176-
std::vector<RegistryEntry> valid_entries;
177-
valid_entries.reserve(entries.size());
178+
auto& entries = it->second;
178179
for (const auto& entry : entries) {
179-
if (entry.expire_time > now) {
180-
valid_entries.push_back(entry);
180+
if (entry.expire_time <= now ||
181+
(filter_caller && entry.raiden_id == caller)) {
182+
EraseFromOwnerIndex(entry.raiden_id, hash);
181183
}
182184
}
185+
entries.erase(
186+
std::remove_if(entries.begin(), entries.end(),
187+
[now, filter_caller, &caller](const RegistryEntry& entry) {
188+
return entry.expire_time <= now ||
189+
(filter_caller && entry.raiden_id == caller);
190+
}),
191+
entries.end());
183192

184-
if (valid_entries.empty()) {
193+
if (entries.empty()) {
194+
registry_.erase(it);
195+
round_robin_indices_.erase(hash);
185196
break;
186197
}
187198

188199
// Round-robin selection
189200
size_t& idx = round_robin_indices_[hash];
190-
idx = idx % valid_entries.size();
191-
const auto& picked = valid_entries[idx];
201+
idx = idx % entries.size();
202+
const auto& picked = entries[idx];
192203
idx++; // Increment for next lookup
193204

194205
auto* meta = response->add_results();

tpu_sync/kv_cache/global_registry/global_registry_test.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,96 @@ TEST_F(GlobalRegistryTest, MultiRegistrationAndRoundRobinLookup) {
200200
host1.job_replica_id);
201201
}
202202

203+
TEST_F(GlobalRegistryTest, LookupDoesNotNameTheCallerToItself) {
204+
const std::string hash = "hash1";
205+
const RaidenId caller = {"job1", "replica1", "data1", 0};
206+
207+
ASSERT_TRUE(client_->Register({{hash, caller, 42}}).ok());
208+
209+
// Caller is skipped and its stale registration is deleted.
210+
auto self = client_->Lookup({hash}, caller);
211+
ASSERT_TRUE(self.ok()) << self.status().ToString();
212+
EXPECT_TRUE(self->empty());
213+
214+
// Since caller was the only holder, the registry entry was purged.
215+
const RaidenId peer = {"job1", "replica2", "data1", 1};
216+
auto from_peer = client_->Lookup({hash}, peer);
217+
ASSERT_TRUE(from_peer.ok()) << from_peer.status().ToString();
218+
EXPECT_TRUE(from_peer->empty());
219+
}
220+
221+
TEST_F(GlobalRegistryTest, LookupPrunesExpiredEntriesEagerly) {
222+
const std::string hash = "expired_lookup_hash";
223+
const RaidenId host = {"job1", "replica1", "data1", 0};
224+
225+
ASSERT_TRUE(client_->Register({{hash, host, 42, absl::Seconds(1)}}).ok());
226+
absl::SleepFor(absl::Seconds(2));
227+
228+
auto res = client_->Lookup({hash});
229+
ASSERT_TRUE(res.ok()) << res.status().ToString();
230+
EXPECT_TRUE(res->empty());
231+
}
232+
233+
TEST_F(GlobalRegistryTest, LookupOffersAnotherHolderWhenTheCallerIsOne) {
234+
const std::string hash1 = "hash1";
235+
const std::string hash2 = "hash2";
236+
const RaidenId caller = {"job1", "replica1", "data1", 0};
237+
const RaidenId peer = {"job1", "replica2", "data1", 1};
238+
239+
ASSERT_TRUE(client_
240+
->Register({{hash1, caller, 42},
241+
{hash1, peer, 43},
242+
{hash2, peer, 44}})
243+
.ok());
244+
245+
// Skipping caller on hash1 offers peer instead, continuing the prefix walk.
246+
auto res = client_->Lookup({hash1, hash2}, caller);
247+
ASSERT_TRUE(res.ok()) << res.status().ToString();
248+
ASSERT_EQ(res->size(), 2);
249+
EXPECT_EQ((*res)[0].raiden_id().job_replica_id(), peer.job_replica_id);
250+
EXPECT_EQ((*res)[0].block_id(), 43);
251+
EXPECT_EQ((*res)[1].raiden_id().job_replica_id(), peer.job_replica_id);
252+
EXPECT_EQ((*res)[1].block_id(), 44);
253+
}
254+
255+
TEST_F(GlobalRegistryTest, LookupRoundRobinsOverTheHoldersLeftAfterTheSkip) {
256+
const std::string hash = "hash1";
257+
const RaidenId caller = {"job1", "replica1", "data1", 0};
258+
const RaidenId peer_a = {"job1", "replica2", "data1", 1};
259+
const RaidenId peer_b = {"job1", "replica3", "data1", 2};
260+
261+
ASSERT_TRUE(client_
262+
->Register({{hash, caller, 42},
263+
{hash, peer_a, 43},
264+
{hash, peer_b, 44}})
265+
.ok());
266+
267+
// Round-robin cycles through remaining peers.
268+
auto first = client_->Lookup({hash}, caller);
269+
ASSERT_TRUE(first.ok()) << first.status().ToString();
270+
ASSERT_EQ(first->size(), 1);
271+
EXPECT_EQ((*first)[0].raiden_id().job_replica_id(), peer_a.job_replica_id);
272+
273+
auto second = client_->Lookup({hash}, caller);
274+
ASSERT_TRUE(second.ok()) << second.status().ToString();
275+
ASSERT_EQ(second->size(), 1);
276+
EXPECT_EQ((*second)[0].raiden_id().job_replica_id(), peer_b.job_replica_id);
277+
}
278+
279+
TEST_F(GlobalRegistryTest, LookupWithoutACallerIdIsUnfiltered) {
280+
const std::string hash = "hash1";
281+
const RaidenId host = {"job1", "replica1", "data1", 0};
282+
283+
ASSERT_TRUE(client_->Register({{hash, host, 42}}).ok());
284+
285+
// Unfiltered when caller is unset.
286+
auto res = client_->Lookup({hash});
287+
ASSERT_TRUE(res.ok()) << res.status().ToString();
288+
ASSERT_EQ(res->size(), 1);
289+
EXPECT_EQ((*res)[0].raiden_id().job_replica_id(), host.job_replica_id);
290+
EXPECT_EQ((*res)[0].block_id(), 42);
291+
}
292+
203293
TEST_F(GlobalRegistryTest, OverwriteRegistrationSameHost) {
204294
std::string hash = "hash1";
205295
RaidenId host = {"job1", "replica1", "data1", 0};

tpu_sync/kv_cache/host_offload_backend.cc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ absl::StatusOr<BlockSliceList> HostOffloadBackend::Lookup(
216216
// at the first one.
217217
std::vector<global_registry::KVBlockMetadata> remote_hits;
218218
if (!missing_hashes.empty() && options.enable_global && client != nullptr) {
219-
auto global_res_or = client->Lookup(missing_hashes);
219+
auto global_res_or = client->Lookup(missing_hashes, local_id);
220220
if (global_res_or.ok()) {
221221
remote_hits = std::move(global_res_or).value();
222222
} else {
@@ -263,9 +263,7 @@ absl::StatusOr<BlockSliceList> HostOffloadBackend::Lookup(
263263
.data_replica_idx = proto_id.data_replica_idx(),
264264
};
265265
if (remote_id == local_id) {
266-
// The registry claims this block lives on this node, but it wasn't
267-
// present during our sweep (or was evicted since). Returning this as
268-
// an unverified HOST hit is unsafe, so this is a miss.
266+
// Unverified HOST hit from a registry that did not filter the caller.
269267
break;
270268
}
271269
results.emplace_back(

0 commit comments

Comments
 (0)