xrpld
Loading...
Searching...
No Matches
TaggedCache.cpp
1#include <xrpl/basics/TaggedCache.h>
2
3#include <xrpl/basics/IntrusivePointer.h>
4#include <xrpl/basics/IntrusiveRefCounts.h>
5#include <xrpl/basics/TaggedCache.ipp> // IWYU pragma: keep
6#include <xrpl/basics/chrono.h>
7#include <xrpl/beast/utility/Journal.h>
8#include <xrpl/protocol/Protocol.h>
9
10#include <gtest/gtest.h>
11#include <helpers/TestSink.h>
12
13#include <memory>
14#include <string>
15#include <utility>
16
17namespace xrpl {
18
19/*
20I guess you can put some items in, make sure they're still there. Let some
21time pass, make sure they're gone. Keep a strong pointer to one of them, make
22sure you can still find it even after time passes. Create two objects with
23the same key, canonicalize them both and make sure you get the same object.
24Put an object in but keep a strong pointer to it, advance the clock a lot,
25then canonicalize a new object with the same key, make sure you get the
26original object.
27*/
28
29TEST(TaggedCacheTest, tagged_cache)
30{
31 using namespace std::chrono_literals;
32 beast::Journal const journal{TestSink::instance()};
33
34 TestStopwatch clock;
35 clock.set(0);
36
37 using Key = LedgerIndex;
38 using Value = std::string;
39 using Cache = TaggedCache<Key, Value>;
40
41 Cache c("test", 1, 1s, clock, journal);
42
43 // Insert an item, retrieve it, and age it so it gets purged.
44 {
45 EXPECT_EQ(c.getCacheSize(), 0);
46 EXPECT_EQ(c.getTrackSize(), 0);
47 EXPECT_FALSE(c.insert(1, "one"));
48 EXPECT_EQ(c.getCacheSize(), 1);
49 EXPECT_EQ(c.getTrackSize(), 1);
50
51 {
53 EXPECT_TRUE(c.retrieve(1, s));
54 EXPECT_EQ(s, "one");
55 }
56
57 ++clock;
58 c.sweep();
59 EXPECT_EQ(c.getCacheSize(), 0);
60 EXPECT_EQ(c.getTrackSize(), 0);
61 }
62
63 // Insert an item, maintain a strong pointer, age it, and
64 // verify that the entry still exists.
65 {
66 EXPECT_FALSE(c.insert(2, "two"));
67 EXPECT_EQ(c.getCacheSize(), 1);
68 EXPECT_EQ(c.getTrackSize(), 1);
69
70 {
71 auto p = c.fetch(2);
72 EXPECT_NE(p, nullptr);
73 ++clock;
74 c.sweep();
75 EXPECT_EQ(c.getCacheSize(), 0);
76 EXPECT_EQ(c.getTrackSize(), 1);
77 }
78
79 // Make sure its gone now that our reference is gone
80 ++clock;
81 c.sweep();
82 EXPECT_EQ(c.getCacheSize(), 0);
83 EXPECT_EQ(c.getTrackSize(), 0);
84 }
85
86 // Insert the same key/value pair and make sure we get the same result
87 {
88 EXPECT_FALSE(c.insert(3, "three"));
89
90 {
91 auto const p1 = c.fetch(3);
92 auto p2 = std::make_shared<Value>("three");
93 c.canonicalizeReplaceClient(3, p2);
94 EXPECT_EQ(p1.get(), p2.get());
95 }
96 ++clock;
97 c.sweep();
98 EXPECT_EQ(c.getCacheSize(), 0);
99 EXPECT_EQ(c.getTrackSize(), 0);
100 }
101
102 // Put an object in but keep a strong pointer to it, advance the clock a
103 // lot, then canonicalize a new object with the same key, make sure you
104 // get the original object.
105 {
106 // Put an object in
107 EXPECT_FALSE(c.insert(4, "four"));
108 EXPECT_EQ(c.getCacheSize(), 1);
109 EXPECT_EQ(c.getTrackSize(), 1);
110
111 {
112 // Keep a strong pointer to it
113 auto const p1 = c.fetch(4);
114 EXPECT_NE(p1, nullptr);
115 EXPECT_EQ(c.getCacheSize(), 1);
116 EXPECT_EQ(c.getTrackSize(), 1);
117 // Advance the clock a lot
118 ++clock;
119 c.sweep();
120 EXPECT_EQ(c.getCacheSize(), 0);
121 EXPECT_EQ(c.getTrackSize(), 1);
122 // Canonicalize a new object with the same key
123 auto p2 = std::make_shared<std::string>("four");
124 EXPECT_TRUE(c.canonicalizeReplaceClient(4, p2));
125 EXPECT_EQ(c.getCacheSize(), 1);
126 EXPECT_EQ(c.getTrackSize(), 1);
127 // Make sure we get the original object
128 EXPECT_EQ(p1.get(), p2.get());
129 }
130
131 ++clock;
132 c.sweep();
133 EXPECT_EQ(c.getCacheSize(), 0);
134 EXPECT_EQ(c.getTrackSize(), 0);
135 }
136
137 {
138 EXPECT_FALSE(c.insert(5, "five"));
139 EXPECT_EQ(c.getCacheSize(), 1);
140 EXPECT_EQ(c.size(), 1);
141
142 {
143 auto const p1 = c.fetch(5);
144 EXPECT_NE(p1, nullptr);
145 EXPECT_EQ(c.getCacheSize(), 1);
146 EXPECT_EQ(c.size(), 1);
147
148 // Advance the clock a lot
149 ++clock;
150 c.sweep();
151 EXPECT_EQ(c.getCacheSize(), 0);
152 EXPECT_EQ(c.size(), 1);
153
154 auto p2 = std::make_shared<std::string>("five_2");
155 EXPECT_TRUE(c.canonicalizeReplaceCache(5, p2));
156 EXPECT_EQ(c.getCacheSize(), 1);
157 EXPECT_EQ(c.size(), 1);
158 // Make sure the caller's original pointer is unchanged
159 EXPECT_NE(p1.get(), p2.get());
160 EXPECT_EQ(*p2, "five_2");
161
162 auto const p3 = c.fetch(5);
163 EXPECT_NE(p3, nullptr);
164 EXPECT_EQ(p3.get(), p2.get());
165 EXPECT_NE(p3.get(), p1.get());
166 }
167
168 ++clock;
169 c.sweep();
170 EXPECT_EQ(c.getCacheSize(), 0);
171 EXPECT_EQ(c.size(), 0);
172 }
173
174 {
175 struct MyRefCountObject : IntrusiveRefCounts
176 {
177 std::string data;
178
179 // Needed to support weak intrusive pointers
180 virtual void
181 partialDestructor()
182 {
183 }
184
185 MyRefCountObject() = default;
186 explicit MyRefCountObject(std::string data) : data(std::move(data))
187 {
188 }
189
190 bool
191 operator==(std::string const& other) const
192 {
193 return data == other;
194 }
195 };
196
197 using IntrPtrCache = TaggedCache<
198 Key,
199 MyRefCountObject,
200 /*IsKeyCache*/ false,
203
204 IntrPtrCache intrPtrCache("IntrPtrTest", 1, 1s, clock, journal);
205
206 intrPtrCache.canonicalizeReplaceCache(1, intr_ptr::makeShared<MyRefCountObject>("one"));
207 EXPECT_EQ(intrPtrCache.getCacheSize(), 1);
208 EXPECT_EQ(intrPtrCache.size(), 1);
209
210 {
211 {
212 intrPtrCache.canonicalizeReplaceCache(
213 1, intr_ptr::makeShared<MyRefCountObject>("one_replaced"));
214
215 auto p = intrPtrCache.fetch(1);
216 EXPECT_EQ(*p, "one_replaced");
217
218 // Advance the clock a lot
219 ++clock;
220 intrPtrCache.sweep();
221 EXPECT_EQ(intrPtrCache.getCacheSize(), 0);
222 EXPECT_EQ(intrPtrCache.size(), 1);
223
224 intrPtrCache.canonicalizeReplaceCache(
225 1, intr_ptr::makeShared<MyRefCountObject>("one_replaced_2"));
226
227 auto p2 = intrPtrCache.fetch(1);
228 EXPECT_EQ(*p2, "one_replaced_2");
229
230 intrPtrCache.del(1, true);
231 }
232
233 intrPtrCache.canonicalizeReplaceCache(
234 1, intr_ptr::makeShared<MyRefCountObject>("one_replaced_3"));
235 auto p3 = intrPtrCache.fetch(1);
236 EXPECT_EQ(*p3, "one_replaced_3");
237 }
238
239 ++clock;
240 intrPtrCache.sweep();
241 EXPECT_EQ(intrPtrCache.getCacheSize(), 0);
242 EXPECT_EQ(intrPtrCache.size(), 0);
243 }
244}
245
246} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
Map/cache combination.
Definition TaggedCache.h:67
static TestSink & instance()
Definition TestSink.h:12
T make_shared(T... args)
SharedWeakUnion< T > SharedWeakUnionPtr
SharedIntrusive< T > SharedPtr
SharedPtr< T > makeShared(A &&... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr bool operator==(BaseUInt< Bits, Tag > const &lhs, BaseUInt< Bits, Tag > const &rhs)
Definition base_uint.h:606
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:370
TEST(FileUtilitiesTest, get_file_contents)
beast::ManualClock< std::chrono::steady_clock > TestStopwatch
A manual Stopwatch for unit tests.
Definition chrono.h:95
Implement the strong count, weak count, and bit flags for an intrusive pointer.