Skip to content

Commit 10ed5cc

Browse files
committed
[llvm/CAS] Introduce ObjectStore::isMaterialized()
This returns true if the object is directly available from the local CAS, for implementations that have this kind of distinction. For such implementations it's useful to be able to identify whether objects need to be downloaded from the distributed cache. (cherry picked from commit c69249f)
1 parent 4e42fde commit 10ed5cc

File tree

10 files changed

+169
-25
lines changed

10 files changed

+169
-25
lines changed

llvm/include/llvm-c/CAS/PluginAPI_functions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,18 @@ LLCAS_PUBLIC llcas_digest_t llcas_objectid_get_digest(llcas_cas_t,
164164
/**
165165
* Checks whether a \c llcas_objectid_t points to an existing object.
166166
*
167+
* \param globally For CAS implementations that distinguish between local CAS
168+
* and remote/distributed CAS, \p globally set to false indicates that the
169+
* lookup will be restricted to the local CAS, returning "not found" even if the
170+
* object might exist in the remote CAS.
167171
* \param error optional pointer to receive an error message if an error
168172
* occurred. If set, the memory it points to needs to be released via
169173
* \c llcas_string_dispose.
170174
* \returns one of \c llcas_lookup_result_t.
171175
*/
172176
LLCAS_PUBLIC llcas_lookup_result_t llcas_cas_contains_object(llcas_cas_t,
173177
llcas_objectid_t,
178+
bool globally,
174179
char **error);
175180

176181
/**

llvm/include/llvm/CAS/ObjectStore.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ class ObjectStore {
152152
/// Returns \c None if the object is not stored in this CAS.
153153
virtual Optional<ObjectRef> getReference(const CASID &ID) const = 0;
154154

155+
/// \returns true if the object is directly available from the local CAS, for
156+
/// implementations that have this kind of distinction.
157+
virtual Expected<bool> isMaterialized(ObjectRef Ref) const = 0;
158+
155159
/// Validate the underlying object referred by CASID.
156160
virtual Error validate(const CASID &ID) = 0;
157161

llvm/lib/CAS/InMemoryCAS.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ class InMemoryCAS : public BuiltinCAS {
220220
return None;
221221
}
222222

223+
Expected<bool> isMaterialized(ObjectRef Ref) const final { return true; }
224+
223225
ArrayRef<char> getDataConst(ObjectHandle Node) const final {
224226
return cast<InMemoryObject>(asInMemoryObject(Node)).getData();
225227
}

llvm/lib/CAS/OnDiskCAS.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class OnDiskCAS : public BuiltinCAS {
2929

3030
Optional<ObjectRef> getReference(const CASID &ID) const final;
3131

32+
Expected<bool> isMaterialized(ObjectRef Ref) const final;
33+
3234
ArrayRef<char> getDataConst(ObjectHandle Node) const final;
3335

3436
void print(raw_ostream &OS) const final;
@@ -91,6 +93,10 @@ Optional<ObjectRef> OnDiskCAS::getReference(const CASID &ID) const {
9193
return convertRef(*ObjID);
9294
}
9395

96+
Expected<bool> OnDiskCAS::isMaterialized(ObjectRef ExternalRef) const {
97+
return DB->containsObject(convertRef(ExternalRef));
98+
}
99+
94100
ArrayRef<char> OnDiskCAS::getDataConst(ObjectHandle Node) const {
95101
return DB->getObjectData(convertHandle(Node));
96102
}

llvm/lib/CAS/PluginAPI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ struct llcas_functions_t {
4848
llcas_digest_t (*objectid_get_digest)(llcas_cas_t, llcas_objectid_t);
4949

5050
llcas_lookup_result_t (*cas_contains_object)(llcas_cas_t, llcas_objectid_t,
51-
char **error);
51+
bool globally, char **error);
5252

5353
llcas_lookup_result_t (*cas_load_object)(llcas_cas_t, llcas_objectid_t,
5454
llcas_loaded_object_t *,

llvm/lib/CAS/PluginCAS.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ class PluginObjectStore
130130
ArrayRef<char> Data) final;
131131
CASID getID(ObjectRef Ref) const final;
132132
Optional<ObjectRef> getReference(const CASID &ID) const final;
133+
Expected<bool> isMaterialized(ObjectRef Ref) const final;
133134
Expected<std::optional<ObjectHandle>> loadIfExists(ObjectRef Ref) final;
134135
void
135136
loadIfExistsAsync(ObjectRef Ref,
@@ -223,18 +224,22 @@ PluginObjectStore::getReference(const CASID &ID) const {
223224
Ctx->c_cas, llcas_digest_t{Hash.data(), Hash.size()}, &c_id, &c_err))
224225
report_fatal_error(Ctx->errorAndDispose(c_err));
225226

226-
llcas_lookup_result_t c_result =
227-
Ctx->Functions.cas_contains_object(Ctx->c_cas, c_id, &c_err);
227+
return ObjectRef::getFromInternalRef(*this, c_id.opaque);
228+
}
229+
230+
Expected<bool> PluginObjectStore::isMaterialized(ObjectRef Ref) const {
231+
llcas_objectid_t c_id{Ref.getInternalRef(*this)};
232+
char *c_err = nullptr;
233+
llcas_lookup_result_t c_result = Ctx->Functions.cas_contains_object(
234+
Ctx->c_cas, c_id, /*globally=*/false, &c_err);
228235
switch (c_result) {
229236
case LLCAS_LOOKUP_RESULT_SUCCESS:
230-
return ObjectRef::getFromInternalRef(*this, c_id.opaque);
237+
return true;
231238
case LLCAS_LOOKUP_RESULT_NOTFOUND:
232-
return std::nullopt;
239+
return false;
233240
case LLCAS_LOOKUP_RESULT_ERROR:
234-
report_fatal_error(Ctx->errorAndDispose(c_err));
241+
return Ctx->errorAndDispose(c_err);
235242
}
236-
237-
return ObjectRef::getFromInternalRef(*this, c_id.opaque);
238243
}
239244

240245
Expected<std::optional<ObjectHandle>>

llvm/lib/RemoteCachingService/CAS/GRPCRelayCAS.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class GRPCRelayCAS : public ObjectStore {
116116
ArrayRef<char> Data) final;
117117
CASID getID(ObjectRef Ref) const final;
118118
Optional<ObjectRef> getReference(const CASID &ID) const final;
119+
Expected<bool> isMaterialized(ObjectRef Ref) const final;
119120
Expected<std::optional<ObjectHandle>> loadIfExists(ObjectRef Ref) final;
120121
Error validate(const CASID &ID) final {
121122
// Not supported yet. Always return success.
@@ -303,6 +304,11 @@ Optional<ObjectRef> GRPCRelayCAS::getReference(const CASID &ID) const {
303304
return toReference(I);
304305
}
305306

307+
Expected<bool> GRPCRelayCAS::isMaterialized(ObjectRef Ref) const {
308+
auto &I = asInMemoryIndexValue(Ref);
309+
return (bool)I.Data.load();
310+
}
311+
306312
Expected<std::optional<ObjectHandle>>
307313
GRPCRelayCAS::loadIfExists(ObjectRef Ref) {
308314
auto &I = asInMemoryIndexValue(Ref);

llvm/tools/libCASPluginTest/libCASPluginTest.cpp

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,18 @@ struct CASWrapper {
112112

113113
std::mutex Lock{};
114114

115+
/// Check if the object is contained, in the "local" CAS only or "globally".
116+
bool containsObject(ObjectID ID, bool Globally);
117+
115118
/// Load the object, potentially "downloading" it from upstream.
116119
Expected<std::optional<ondisk::ObjectHandle>> loadObject(ObjectID ID);
117120

118-
/// "Uploads" a key the associated full node graph.
121+
/// "Uploads" a key and the associated full node graph.
119122
Error upstreamKey(ArrayRef<uint8_t> Key, ObjectID Value);
120-
/// "Downloads" the single root node that is associated with the key. The rest
121-
/// of the nodes in the graph will be "downloaded" lazily as they are visited.
123+
124+
/// "Downloads" the ID associated with the key but not the node data. The node
125+
/// itself and the rest of the nodes in the graph will be "downloaded" lazily
126+
/// as they are visited.
122127
Expected<std::optional<ObjectID>> downstreamKey(ArrayRef<uint8_t> Key);
123128

124129
/// Synchronized access to \c llvm::errs().
@@ -140,6 +145,17 @@ DEFINE_SIMPLE_CONVERSION_FUNCTIONS(CASWrapper, llcas_cas_t)
140145

141146
} // namespace
142147

148+
bool CASWrapper::containsObject(ObjectID ID, bool Globally) {
149+
if (DB->getGraphDB().containsObject(ID))
150+
return true;
151+
if (!Globally || !UpstreamDB)
152+
return false;
153+
154+
ObjectID UpstreamID =
155+
UpstreamDB->getGraphDB().getReference(DB->getGraphDB().getDigest(ID));
156+
return UpstreamDB->getGraphDB().containsObject(UpstreamID);
157+
}
158+
143159
Expected<std::optional<ondisk::ObjectHandle>>
144160
CASWrapper::loadObject(ObjectID ID) {
145161
std::optional<ondisk::ObjectHandle> Obj;
@@ -231,16 +247,8 @@ CASWrapper::downstreamKey(ArrayRef<uint8_t> Key) {
231247
if (!UpstreamValue)
232248
return std::nullopt;
233249

234-
Expected<ObjectID> Value = downstreamNode(*UpstreamValue);
235-
if (!Value)
236-
return Value.takeError();
237-
assert(DB->getGraphDB().getDigest(*Value) ==
238-
UpstreamDB->getGraphDB().getDigest(*UpstreamValue));
239-
Expected<ObjectID> PutValue = DB->KVPut(Key, *Value);
240-
if (!PutValue)
241-
return PutValue.takeError();
242-
assert(*PutValue == *Value);
243-
return PutValue;
250+
return DB->getGraphDB().getReference(
251+
UpstreamDB->getGraphDB().getDigest(*UpstreamValue));
244252
}
245253

246254
llcas_cas_t llcas_cas_create(llcas_cas_options_t c_opts, char **error) {
@@ -329,11 +337,11 @@ llcas_digest_t llcas_objectid_get_digest(llcas_cas_t c_cas,
329337

330338
llcas_lookup_result_t llcas_cas_contains_object(llcas_cas_t c_cas,
331339
llcas_objectid_t c_id,
332-
char **error) {
333-
auto &CAS = unwrap(c_cas)->DB->getGraphDB();
340+
bool globally, char **error) {
334341
ObjectID ID = ObjectID::fromOpaqueData(c_id.opaque);
335-
return CAS.containsObject(ID) ? LLCAS_LOOKUP_RESULT_SUCCESS
336-
: LLCAS_LOOKUP_RESULT_NOTFOUND;
342+
return unwrap(c_cas)->containsObject(ID, globally)
343+
? LLCAS_LOOKUP_RESULT_SUCCESS
344+
: LLCAS_LOOKUP_RESULT_NOTFOUND;
337345
}
338346

339347
llcas_lookup_result_t llcas_cas_load_object(llcas_cas_t c_cas,

llvm/unittests/CAS/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ add_llvm_unittest(CASTests
2929
OnDiskGraphDBTest.cpp
3030
OnDiskHashMappedTrieTest.cpp
3131
OnDiskKeyValueDBTest.cpp
32+
PluginCASTest.cpp
3233
ThreadSafeAllocatorTest.cpp
3334
TreeSchemaTest.cpp
3435
UnifiedOnDiskCacheTest.cpp
3536
)
3637

3738
target_link_libraries(CASTests PRIVATE LLVMTestingSupport)
39+
add_dependencies(CASTests CASPluginTest)

llvm/unittests/CAS/PluginCASTest.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//===- llvm/unittest/CAS/PluginCASTest.cpp --------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "llvm/CAS/ActionCache.h"
10+
#include "llvm/CAS/ObjectStore.h"
11+
#include "llvm/Config/config.h"
12+
#include "llvm/Support/Path.h"
13+
#include "llvm/Testing/Support/Error.h"
14+
#include "llvm/Testing/Support/SupportHelpers.h"
15+
#include "gtest/gtest.h"
16+
17+
#if LLVM_ENABLE_ONDISK_CAS
18+
19+
using namespace llvm;
20+
using namespace llvm::cas;
21+
22+
// See llvm/utils/unittest/UnitTestMain/TestMain.cpp
23+
extern const char *TestMainArgv0;
24+
25+
// Just a reachable symbol to ease resolving of the executable's path.
26+
static std::string TestStringArg1("plugincas-test-string-arg1");
27+
28+
static std::string getCASPluginPath() {
29+
std::string Executable =
30+
sys::fs::getMainExecutable(TestMainArgv0, &TestStringArg1);
31+
llvm::SmallString<256> PathBuf(sys::path::parent_path(
32+
sys::path::parent_path(sys::path::parent_path(Executable))));
33+
std::string LibName = "libCASPluginTest";
34+
sys::path::append(PathBuf, "lib", LibName + LLVM_PLUGIN_EXT);
35+
return std::string(PathBuf);
36+
}
37+
38+
TEST(PluginCASTest, isMaterialized) {
39+
unittest::TempDir Temp("plugin-cas", /*Unique=*/true);
40+
std::string UpDir(Temp.path("up"));
41+
std::string DownDir(Temp.path("down"));
42+
std::pair<std::string, std::string> PluginOpts[] = {
43+
{"upstream-path", std::string(UpDir)}};
44+
45+
{
46+
std::optional<
47+
std::pair<std::shared_ptr<ObjectStore>, std::shared_ptr<ActionCache>>>
48+
DBs;
49+
ASSERT_THAT_ERROR(
50+
createPluginCASDatabases(getCASPluginPath(), DownDir, PluginOpts)
51+
.moveInto(DBs),
52+
Succeeded());
53+
std::shared_ptr<ObjectStore> CAS;
54+
std::shared_ptr<ActionCache> AC;
55+
std::tie(CAS, AC) = std::move(*DBs);
56+
57+
Optional<CASID> ID1, ID2;
58+
ASSERT_THAT_ERROR(CAS->createProxy(std::nullopt, "1").moveInto(ID1),
59+
Succeeded());
60+
ASSERT_THAT_ERROR(CAS->createProxy(std::nullopt, "2").moveInto(ID2),
61+
Succeeded());
62+
Optional<ObjectRef> ID2Ref = CAS->getReference(*ID2);
63+
ASSERT_TRUE(ID2Ref);
64+
bool IsMaterialized = false;
65+
ASSERT_THAT_ERROR(CAS->isMaterialized(*ID2Ref).moveInto(IsMaterialized),
66+
Succeeded());
67+
EXPECT_TRUE(IsMaterialized);
68+
ASSERT_THAT_ERROR(AC->put(*ID1, *ID2, /*Globally=*/true), Succeeded());
69+
}
70+
71+
// Clear "local" cache.
72+
sys::fs::remove_directories(DownDir);
73+
74+
{
75+
std::optional<
76+
std::pair<std::shared_ptr<ObjectStore>, std::shared_ptr<ActionCache>>>
77+
DBs;
78+
ASSERT_THAT_ERROR(
79+
createPluginCASDatabases(getCASPluginPath(), DownDir, PluginOpts)
80+
.moveInto(DBs),
81+
Succeeded());
82+
std::shared_ptr<ObjectStore> CAS;
83+
std::shared_ptr<ActionCache> AC;
84+
std::tie(CAS, AC) = std::move(*DBs);
85+
86+
Optional<CASID> ID1, ID2;
87+
ASSERT_THAT_ERROR(CAS->createProxy(std::nullopt, "1").moveInto(ID1),
88+
Succeeded());
89+
ASSERT_THAT_ERROR(AC->get(*ID1, /*Globally=*/true).moveInto(ID2),
90+
Succeeded());
91+
Optional<ObjectRef> ID2Ref = CAS->getReference(*ID2);
92+
ASSERT_TRUE(ID2Ref);
93+
bool IsMaterialized = false;
94+
ASSERT_THAT_ERROR(CAS->isMaterialized(*ID2Ref).moveInto(IsMaterialized),
95+
Succeeded());
96+
EXPECT_FALSE(IsMaterialized);
97+
98+
Optional<ObjectProxy> Obj;
99+
ASSERT_THAT_ERROR(CAS->getProxy(*ID2Ref).moveInto(Obj), Succeeded());
100+
ASSERT_THAT_ERROR(CAS->isMaterialized(*ID2Ref).moveInto(IsMaterialized),
101+
Succeeded());
102+
EXPECT_TRUE(IsMaterialized);
103+
}
104+
}
105+
106+
#endif // LLVM_ENABLE_ONDISK_CAS

0 commit comments

Comments
 (0)