GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/quota_posix.h
Date: 2026-04-19 02:41:37
Exec Total Coverage
Lines: 30 31 96.8%
Branches: 0 0 -%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #ifndef CVMFS_QUOTA_POSIX_H_
6 #define CVMFS_QUOTA_POSIX_H_
7
8 #include <pthread.h>
9 #include <stdint.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include <map>
14 #include <string>
15 #include <vector>
16
17 #include "crypto/hash.h"
18 #include "duplex_sqlite3.h"
19 #include "duplex_testing.h"
20 #include "quota.h"
21 #include "statistics.h"
22 #include "util/single_copy.h"
23
24 namespace perf {
25 class Recorder;
26 }
27
28 /**
29 * Works with the PosixCacheManager. Uses an SQlite database for cache contents
30 * tracking. Tracking is asynchronously.
31 *
32 * TODO(jblomer): split into client, server, and protocol classes.
33 */
34 class PosixQuotaManager : public QuotaManager {
35 FRIEND_TEST(T_QuotaManager, BindReturnPipe);
36 FRIEND_TEST(T_QuotaManager, Cleanup);
37 FRIEND_TEST(T_QuotaManager, CleanupLru);
38 FRIEND_TEST(T_QuotaManager, Contains);
39 FRIEND_TEST(T_QuotaManager, InitDatabase);
40 FRIEND_TEST(T_QuotaManager, MakeReturnPipe);
41
42 public:
43 static PosixQuotaManager *Create(const std::string &cache_workspace,
44 const uint64_t limit,
45 const uint64_t cleanup_threshold,
46 const bool rebuild_database);
47 static PosixQuotaManager *CreateShared(const std::string &exe_path,
48 const std::string &cache_workspace,
49 const uint64_t limit,
50 const uint64_t cleanup_threshold,
51 bool foreground);
52 static int MainCacheManager(int argc, char **argv);
53
54 virtual ~PosixQuotaManager();
55 virtual bool HasCapability(Capabilities capability) { return true; }
56
57 virtual void Insert(const shash::Any &hash, const uint64_t size,
58 const std::string &description);
59 virtual void InsertVolatile(const shash::Any &hash, const uint64_t size,
60 const std::string &description);
61 virtual bool Pin(const shash::Any &hash, const uint64_t size,
62 const std::string &description, const bool is_catalog);
63 virtual void Unpin(const shash::Any &hash);
64 virtual void Touch(const shash::Any &hash);
65 virtual void Remove(const shash::Any &file);
66 virtual bool Cleanup(const uint64_t leave_size);
67
68 virtual void RegisterBackChannel(int back_channel[2],
69 const std::string &channel_id);
70 virtual void UnregisterBackChannel(int back_channel[2],
71 const std::string &channel_id);
72
73 virtual std::vector<std::string> List();
74 virtual std::vector<std::string> ListPinned();
75 virtual std::vector<std::string> ListCatalogs();
76 virtual std::vector<std::string> ListVolatile();
77 virtual uint64_t GetMaxFileSize();
78 virtual uint64_t GetCapacity();
79 virtual uint64_t GetSize();
80 virtual uint64_t GetSizePinned();
81 virtual bool SetLimit(uint64_t limit);
82 virtual uint64_t GetCleanupRate(uint64_t period_s);
83
84 virtual void Spawn();
85 virtual pid_t GetPid();
86 virtual uint32_t GetProtocolRevision();
87 virtual void RegisterMountpoint(const std::string &mountpoint);
88 virtual void SetCleanupPolicy(bool cleanup_unused_first);
89 virtual std::string GetMountpoints();
90 virtual std::string GetGroupHashes();
91
92 void ManagedReadHalfPipe(int fd, void *buf, size_t nbyte);
93 35 void SetCacheMgrPid(pid_t pid_) { cachemgr_pid_ = pid_; };
94
95
96 private:
97 /**
98 * Loaded catalogs are pinned in the LRU and have to be treated differently.
99 */
100 enum FileTypes {
101 kFileRegular = 0,
102 kFileCatalog,
103 };
104
105 /**
106 * List of RPCs that can be sent to the cache manager.
107 */
108 enum CommandType {
109 kTouch = 0,
110 kInsert,
111 kReserve,
112 kPin,
113 kUnpin,
114 kRemove,
115 kCleanup,
116 kList,
117 kListPinned,
118 kListCatalogs,
119 kStatus,
120 kLimits,
121 kPid,
122 kPinRegular,
123 kRegisterBackChannel,
124 kUnregisterBackChannel,
125 kGetProtocolRevision,
126 kInsertVolatile,
127 // as of protocol revision 2
128 kListVolatile,
129 kCleanupRate,
130 kSetLimit,
131 // as of protocol revision 3
132 kRegisterMountpoint,
133 kGetMountpoints,
134 kGetGroupHashes,
135 kSetCleanupPolicy,
136 };
137
138 /**
139 * That could be done in more elegant way. However, we might have a situation
140 * with old cache manager serving new clients (or vice versa) and we don't
141 * want to change the memory layout of LruCommand.
142 */
143 struct LruCommand {
144 CommandType command_type;
145 uint64_t size; /**< Careful! Last 3 bits store hash algorithm */
146 int return_pipe; /**< For cleanup, listing, and reservations */
147 unsigned char digest[shash::kMaxDigestSize];
148 /**
149 * Maximum 512-sizeof(LruCommand) in order to guarantee atomic pipe
150 * operations.
151 */
152 uint16_t desc_length;
153
154 5290180 LruCommand()
155 5290180 : command_type(static_cast<CommandType>(0))
156 5290180 , size(0)
157 5290180 , return_pipe(-1)
158 5290180 , desc_length(0) {
159 5290180 memset(digest, 0, shash::kMaxDigestSize);
160 5290180 }
161
162 3501754 void SetSize(const uint64_t new_size) {
163 3501754 uint64_t mask = 7;
164 3501754 mask = ~(mask << (64 - 3));
165 3501754 size = (new_size & mask) | size;
166 3501754 }
167
168 10506265 uint64_t GetSize() const {
169 10506265 uint64_t mask = 7;
170 10506265 mask = ~(mask << (64 - 3));
171 10506265 return size & mask;
172 }
173
174 5252707 void StoreHash(const shash::Any &hash) {
175 5252707 memcpy(digest, hash.digest, hash.GetDigestSize());
176 // Exclude MD5
177 5252707 uint64_t algo_flags = hash.algorithm - 1;
178 5252707 algo_flags = algo_flags << (64 - 3);
179 5252707 size |= algo_flags;
180 5252707 }
181
182 5252380 shash::Any RetrieveHash() const {
183 5252380 const uint64_t algo_flags = size >> (64 - 3);
184 5252380 shash::Any result(static_cast<shash::Algorithms>(algo_flags + 1));
185 5252380 memcpy(result.digest, digest, result.GetDigestSize());
186 5252380 return result;
187 }
188 };
189
190 /**
191 * Used for batch queries in DoCleanup()
192 */
193 struct EvictCandidate {
194 uint64_t size;
195 uint64_t acseq;
196 shash::Any hash;
197 1820350 EvictCandidate(const shash::Any &h, uint64_t s, uint64_t a)
198 1820350 : size(s), acseq(a), hash(h) { }
199 };
200
201 /**
202 * Magic number to make reading PIDs from lockfiles more robust and
203 * versionable
204 */
205 static const unsigned kLockFileMagicNumber = 142857;
206
207 /**
208 * Maximum page cache per thread (Bytes).
209 */
210 static const unsigned kSqliteMemPerThread = 2 * 1024 * 1024;
211
212 /**
213 * Collect a number of insert and touch operations before processing them
214 * as sqlite commands.
215 */
216 static const unsigned kCommandBufferSize = 32;
217
218 /**
219 * Batch size for database operations during DoCleanup()
220 */
221 static const unsigned kEvictBatchSize = 1000;
222
223 /**
224 * Make sure that the amount of data transferred through the RPC pipe is
225 * within the OS's guarantees for atomicity.
226 */
227 static const unsigned kMaxDescription = 512 - sizeof(LruCommand);
228
229 /**
230 * Alarm when more than 75% of the cache fraction allowed for pinned files
231 * (50%) is filled with pinned files
232 */
233 static const unsigned kHighPinWatermark = 75;
234
235 /**
236 * The last bit in the sequence number indicates if an entry is volatile.
237 * Such sequence numbers are negative and they are preferred during cleanup.
238 * Volatile entries are used for instance for ALICE conditions data.
239 */
240 static const uint64_t kVolatileFlag = 1ULL << 63;
241
242 bool InitDatabase(const bool rebuild_database);
243 bool RebuildDatabase();
244 void CloseDatabase();
245 bool Contains(const std::string &hash_str);
246 bool DoCleanup(const uint64_t leave_size);
247 bool EmptyTrash(const std::vector<std::string> &trash);
248
249 void MakeReturnPipe(int pipe[2]);
250 int BindReturnPipe(int pipe_wronly);
251 void UnbindReturnPipe(int pipe_wronly);
252 void UnlinkReturnPipe(int pipe_wronly);
253 void CloseReturnPipe(int pipe[2]);
254 void CleanupPipes();
255
256 void CheckFreeSpace();
257 void CheckHighPinWatermark();
258 void ProcessCommandBunch(const unsigned num,
259 const LruCommand *commands,
260 const char *descriptions);
261 static void *MainCommandServer(void *data);
262
263 void DoInsert(const shash::Any &hash, const uint64_t size,
264 const std::string &description, const CommandType command_type);
265 std::vector<std::string> DoList(const CommandType list_command);
266 void GetSharedStatus(uint64_t *gauge, uint64_t *pinned);
267 bool SetSharedLimit(uint64_t limit);
268 void GetLimits(uint64_t *limit, uint64_t *cleanup_threshold);
269
270 static void ParseDirectories(const std::string cache_workspace,
271 std::string *cache_dir,
272 std::string *workspace_dir);
273 PosixQuotaManager(const uint64_t limit, const uint64_t cleanup_threshold,
274 const std::string &cache_workspace);
275 void SkipEviction(const EvictCandidate &candidate);
276
277 /**
278 * Indicates if the cache manager is a shared process or a thread within the
279 * same process (exclusive cache manager)
280 */
281 bool shared_;
282
283 /**
284 * True once the program switches into multi-threaded mode or the quota
285 * manager process has been forked resp.
286 */
287 bool spawned_;
288
289 /**
290 * Soft limit in bytes, start cleanup when reached.
291 */
292 uint64_t limit_;
293
294 /**
295 * Cleanup until cleanup_threshold_ are left in the cache.
296 */
297 uint64_t cleanup_threshold_;
298
299 /**
300 * Current size of cache.
301 */
302 uint64_t gauge_;
303
304 /**
305 * Size of pinned files in bytes (usually file catalogs).
306 */
307 uint64_t pinned_;
308
309 /**
310 * Current access sequence number. Gets increased on every access/insert
311 * operation.
312 */
313 uint64_t seq_;
314
315 /**
316 * Should match the directory given to the cache manager.
317 */
318 std::string cache_dir_;
319
320 /**
321 * Directory for the database lock (shared manager) and the pipes (also
322 * shared manager). Usually the same as cache_dir_. Can be different if
323 * CVMFS_WORKSPACE or CVMFS_CACHE_WORKSPACE is set.
324 */
325 std::string workspace_dir_;
326
327 /**
328 * Pinned content hashes and their size.
329 */
330 std::map<shash::Any, uint64_t> pinned_chunks_;
331
332 /**
333 * Used to send RPCs to the quota manager thread or process.
334 */
335 int pipe_lru_[2];
336
337 /**
338 * In exclusive mode, controls the quota manager thread.
339 */
340 pthread_t thread_lru_;
341
342 /**
343 * Ensures exclusive cache database access through POSIX file lock.
344 */
345 int fd_lock_cachedb_;
346
347 /**
348 * If this is true, the unlink operations that correspond to a cleanup run
349 * will be performed in a detached, asynchronous process.
350 */
351 bool async_delete_;
352
353
354 /**
355 * Record pid of current cache manager in order to check if its process
356 * disappeared.
357 */
358 pid_t cachemgr_pid_;
359
360 /**
361 * Keeps track of the number of cleanups over time. Use by
362 * `cvmfs_talk cleanup rate`
363 */
364 perf::MultiRecorder cleanup_recorder_;
365
366 sqlite3 *database_;
367 sqlite3_stmt *stmt_touch_;
368 sqlite3_stmt *stmt_unpin_;
369 sqlite3_stmt *stmt_block_;
370 sqlite3_stmt *stmt_unblock_;
371 sqlite3_stmt *stmt_new_;
372 sqlite3_stmt *stmt_lru_;
373 sqlite3_stmt *stmt_size_;
374 sqlite3_stmt *stmt_rm_;
375 sqlite3_stmt *stmt_rm_batch_;
376 sqlite3_stmt *stmt_list_;
377 sqlite3_stmt *stmt_list_pinned_; /**< Loaded catalogs are pinned. */
378 sqlite3_stmt *stmt_list_catalogs_;
379 sqlite3_stmt *stmt_list_volatile_;
380
381 /**
382 * Used in the destructor to steer closing of the database and so on.
383 */
384 bool initialized_;
385
386 /**
387 * Used in DoCleanup to exclude currently used files from eviction
388 */
389 // TODO(gchr): it would be faster if it was a std::set. Needs a comparison
390 // operator for shash::Any
391 pthread_mutex_t *lock_open_files_;
392 std::vector<shash::Short> open_files_;
393
394 bool cleanup_unused_first_;
395 std::vector<std::string> mountpoints_;
396
397 std::vector<shash::Short> CollectAllOpenHashes();
398
399 struct CollectorHandler {
400 std::vector<shash::Short> &of;
401 const std::vector<std::string> &mp;
402 pthread_mutex_t *l;
403 size_t i;
404 };
405
406 static void *CollectMountpointsHashes(void *data);
407 }; // class PosixQuotaManager
408
409 #endif // CVMFS_QUOTA_POSIX_H_
410
411