GCC Code Coverage Report


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