Skip to content

Commit c23036f

Browse files
authored
Merge pull request #7252 from akyrtzi/akyrtzi/pr/stable-cas-materialized-api
[stable/20221013][llvm/CAS] Introduce `ObjectStore::isMaterialized()`
2 parents 4e42fde + 10ed5cc commit c23036f

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)