Directory: | cvmfs/ |
---|---|
File: | cvmfs/statistics_database.cc |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 63 | 269 | 23.4% |
Branches: | 109 | 809 | 13.5% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | */ | ||
4 | |||
5 | #include "statistics_database.h" | ||
6 | |||
7 | #include "util/exception.h" | ||
8 | |||
9 | |||
10 | const float StatisticsDatabase::kLatestCompatibleSchema = 1.0f; | ||
11 | float StatisticsDatabase::kLatestSchema = 1.0f; | ||
12 | |||
13 | // Changelog | ||
14 | // 1 --> 2: (Sep 4 2019) | ||
15 | // * change column name `finished_time` -> `finish_time` | ||
16 | // in publish_statistics table | ||
17 | // * add column `revision` to publish_statistics table | ||
18 | // * change column name `duplicated_files` -> `chunks_duplicated` | ||
19 | // in publish_statistics table | ||
20 | // * add column `chunks_added` to publish_statistics table | ||
21 | // * add column `symlinks_added` to publish_statistics table | ||
22 | // * add column `symlinks_removed` to publish_statistics table | ||
23 | // * add column `symlinks_changed` to publish_statistics table | ||
24 | // * change column name `finished_time` -> `finish_time` | ||
25 | // in gc_statistics table | ||
26 | // 2 --> 3: (Jan 14 2020) | ||
27 | // * add `success` column to publish_statistics table (1 for success | ||
28 | // 0 for fail) | ||
29 | // * add `success` column to gc_statistics table (1 for success | ||
30 | // 0 for fail) | ||
31 | // 3 --> 4: (Feb 1 2022) | ||
32 | // * add column `n_duplicate_delete_requests` to gc_statistics table | ||
33 | |||
34 | unsigned StatisticsDatabase::kLatestSchemaRevision = 4; | ||
35 | unsigned int StatisticsDatabase::instances = 0; | ||
36 | bool StatisticsDatabase::compacting_fails = false; | ||
37 | |||
38 | |||
39 | namespace { | ||
40 | |||
41 | struct PublishStats { | ||
42 | std::string revision; | ||
43 | std::string files_added; | ||
44 | std::string files_removed; | ||
45 | std::string files_changed; | ||
46 | std::string chunks_added; | ||
47 | std::string chunks_duplicated; | ||
48 | std::string catalogs_added; | ||
49 | std::string dirs_added; | ||
50 | std::string dirs_removed; | ||
51 | std::string dirs_changed; | ||
52 | std::string symlinks_added; | ||
53 | std::string symlinks_removed; | ||
54 | std::string symlinks_changed; | ||
55 | std::string bytes_added; | ||
56 | std::string bytes_removed; | ||
57 | std::string bytes_uploaded; | ||
58 | std::string catalog_bytes_uploaded; | ||
59 | |||
60 | ✗ | explicit PublishStats(const perf::Statistics *statistics) | |
61 | ✗ | : revision(statistics->Lookup("publish.revision")->ToString()) | |
62 | ✗ | , files_added(statistics->Lookup("publish.n_files_added")->ToString()) | |
63 | ✗ | , files_removed(statistics->Lookup("publish.n_files_removed")->ToString()) | |
64 | ✗ | , files_changed(statistics->Lookup("publish.n_files_changed")->ToString()) | |
65 | ✗ | , chunks_added(statistics->Lookup("publish.n_chunks_added")->ToString()) | |
66 | ✗ | , chunks_duplicated( | |
67 | statistics->Lookup("publish.n_chunks_duplicated")->ToString()) | ||
68 | ✗ | , catalogs_added( | |
69 | statistics->Lookup("publish.n_catalogs_added")->ToString()) | ||
70 | ✗ | , dirs_added( | |
71 | statistics->Lookup("publish.n_directories_added")->ToString()) | ||
72 | ✗ | , dirs_removed( | |
73 | statistics->Lookup("publish.n_directories_removed")->ToString()) | ||
74 | ✗ | , dirs_changed( | |
75 | statistics->Lookup("publish.n_directories_changed")->ToString()) | ||
76 | ✗ | , symlinks_added( | |
77 | statistics->Lookup("publish.n_symlinks_added")->ToString()) | ||
78 | ✗ | , symlinks_removed( | |
79 | statistics->Lookup("publish.n_symlinks_removed")->ToString()) | ||
80 | ✗ | , symlinks_changed( | |
81 | statistics->Lookup("publish.n_symlinks_changed")->ToString()) | ||
82 | ✗ | , bytes_added(statistics->Lookup("publish.sz_added_bytes")->ToString()) | |
83 | ✗ | , bytes_removed( | |
84 | statistics->Lookup("publish.sz_removed_bytes")->ToString()) | ||
85 | ✗ | , bytes_uploaded( | |
86 | statistics->Lookup("publish.sz_uploaded_bytes")->ToString()) | ||
87 | ✗ | , catalog_bytes_uploaded( | |
88 | statistics->Lookup("publish.sz_uploaded_catalog_bytes") | ||
89 | ✗ | ->ToString()) { } | |
90 | }; | ||
91 | |||
92 | |||
93 | struct GcStats { | ||
94 | std::string n_preserved_catalogs; | ||
95 | std::string n_condemned_catalogs; | ||
96 | std::string n_condemned_objects; | ||
97 | std::string sz_condemned_bytes; | ||
98 | std::string n_duplicate_delete_requests; | ||
99 | |||
100 | ✗ | explicit GcStats(const perf::Statistics *statistics) { | |
101 | ✗ | perf::Counter *c = NULL; | |
102 | ✗ | c = statistics->Lookup("gc.n_preserved_catalogs"); | |
103 | ✗ | n_preserved_catalogs = c ? c->ToString() : "0"; | |
104 | ✗ | c = statistics->Lookup("gc.n_condemned_catalogs"); | |
105 | ✗ | n_condemned_catalogs = c ? c->ToString() : "0"; | |
106 | ✗ | c = statistics->Lookup("gc.n_condemned_objects"); | |
107 | ✗ | n_condemned_objects = c ? c->ToString() : "0"; | |
108 | ✗ | c = statistics->Lookup("gc.sz_condemned_bytes"); | |
109 | ✗ | sz_condemned_bytes = c ? c->ToString() : "0"; | |
110 | ✗ | c = statistics->Lookup("gc.n_duplicate_delete_requests"); | |
111 | ✗ | n_duplicate_delete_requests = c ? c->ToString() : "0"; | |
112 | } | ||
113 | }; | ||
114 | |||
115 | |||
116 | /** | ||
117 | * Build the insert statement into publish_statistics table. | ||
118 | * | ||
119 | * @param stats a struct with all values stored in strings | ||
120 | * @return the insert statement | ||
121 | */ | ||
122 | ✗ | std::string PrepareStatementIntoPublish(const perf::Statistics *statistics, | |
123 | const std::string &start_time, | ||
124 | const std::string &finish_time, | ||
125 | const bool success) { | ||
126 | ✗ | struct PublishStats const stats = PublishStats(statistics); | |
127 | std::string insert_statement = "INSERT INTO publish_statistics (" | ||
128 | "start_time," | ||
129 | "finish_time," | ||
130 | "revision," | ||
131 | "files_added," | ||
132 | "files_removed," | ||
133 | "files_changed," | ||
134 | "chunks_added," | ||
135 | "chunks_duplicated," | ||
136 | "catalogs_added," | ||
137 | "directories_added," | ||
138 | "directories_removed," | ||
139 | "directories_changed," | ||
140 | "symlinks_added," | ||
141 | "symlinks_removed," | ||
142 | "symlinks_changed," | ||
143 | "sz_bytes_added," | ||
144 | "sz_bytes_removed," | ||
145 | "sz_bytes_uploaded," | ||
146 | "sz_catalog_bytes_uploaded," | ||
147 | "success)" | ||
148 | " VALUES(" | ||
149 | "'" | ||
150 | ✗ | + start_time + "'," + "'" + finish_time + "'," | |
151 | ✗ | + stats.revision + "," + stats.files_added | |
152 | ✗ | + "," + stats.files_removed + "," | |
153 | ✗ | + stats.files_changed + "," | |
154 | ✗ | + stats.chunks_added + "," | |
155 | ✗ | + stats.chunks_duplicated + "," | |
156 | ✗ | + stats.catalogs_added + "," + stats.dirs_added | |
157 | ✗ | + "," + stats.dirs_removed + "," | |
158 | ✗ | + stats.dirs_changed + "," | |
159 | ✗ | + stats.symlinks_added + "," | |
160 | ✗ | + stats.symlinks_removed + "," | |
161 | ✗ | + stats.symlinks_changed + "," | |
162 | ✗ | + stats.bytes_added + "," + stats.bytes_removed | |
163 | ✗ | + "," + stats.bytes_uploaded + "," | |
164 | ✗ | + stats.catalog_bytes_uploaded + "," | |
165 | ✗ | + (success ? "1" : "0") + ");"; | |
166 | ✗ | return insert_statement; | |
167 | } | ||
168 | |||
169 | |||
170 | /** | ||
171 | * Build the insert statement into gc_statistics table. | ||
172 | * | ||
173 | * @param stats a struct with values stored in strings | ||
174 | * @param start_time, finish_time to run Main() of the command | ||
175 | * @param repo_name fully qualified name of the repository | ||
176 | * | ||
177 | * @return the insert statement | ||
178 | */ | ||
179 | ✗ | std::string PrepareStatementIntoGc(const perf::Statistics *statistics, | |
180 | const std::string &start_time, | ||
181 | const std::string &finish_time, | ||
182 | const std::string &repo_name, | ||
183 | const bool success) { | ||
184 | ✗ | struct GcStats const stats = GcStats(statistics); | |
185 | ✗ | std::string insert_statement = ""; | |
186 | ✗ | if (StatisticsDatabase::GcExtendedStats(repo_name)) { | |
187 | insert_statement = "INSERT INTO gc_statistics (" | ||
188 | "start_time," | ||
189 | "finish_time," | ||
190 | "n_preserved_catalogs," | ||
191 | "n_condemned_catalogs," | ||
192 | "n_condemned_objects," | ||
193 | "sz_condemned_bytes," | ||
194 | "n_duplicate_delete_requests," | ||
195 | "success)" | ||
196 | " VALUES(" | ||
197 | "'" | ||
198 | ✗ | + start_time + "'," + "'" + finish_time + "'," | |
199 | ✗ | + stats.n_preserved_catalogs + "," | |
200 | ✗ | + stats.n_condemned_catalogs + "," | |
201 | ✗ | + stats.n_condemned_objects + "," | |
202 | ✗ | + stats.sz_condemned_bytes + "," | |
203 | ✗ | + stats.n_duplicate_delete_requests + "," | |
204 | ✗ | + (success ? "1" : "0") + ");"; | |
205 | } else { | ||
206 | // insert values except sz_condemned_bytes | ||
207 | insert_statement = "INSERT INTO gc_statistics (" | ||
208 | "start_time," | ||
209 | "finish_time," | ||
210 | "n_preserved_catalogs," | ||
211 | "n_condemned_catalogs," | ||
212 | "n_condemned_objects," | ||
213 | "n_duplicate_delete_requests," | ||
214 | "success)" | ||
215 | " VALUES(" | ||
216 | "'" | ||
217 | ✗ | + start_time + "'," + "'" + finish_time + "'," | |
218 | ✗ | + stats.n_preserved_catalogs + "," | |
219 | ✗ | + stats.n_condemned_catalogs + "," | |
220 | ✗ | + stats.n_condemned_objects + "," | |
221 | ✗ | + stats.n_duplicate_delete_requests + "," | |
222 | ✗ | + (success ? "1" : "0") + ");"; | |
223 | } | ||
224 | ✗ | return insert_statement; | |
225 | } | ||
226 | |||
227 | } // namespace | ||
228 | |||
229 | |||
230 | 28 | bool StatisticsDatabase::CreateEmptyDatabase() { | |
231 | 28 | ++create_empty_db_calls; | |
232 | const bool ret1 = | ||
233 |
3/6✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
|
56 | sqlite::Sql(sqlite_db(), "CREATE TABLE publish_statistics (" |
234 | "publish_id INTEGER PRIMARY KEY," | ||
235 | "start_time TEXT," | ||
236 | "finish_time TEXT," | ||
237 | "revision INTEGER," | ||
238 | "files_added INTEGER," | ||
239 | "files_removed INTEGER," | ||
240 | "files_changed INTEGER," | ||
241 | "chunks_added INTEGER," | ||
242 | "chunks_duplicated INTEGER," | ||
243 | "catalogs_added INTEGER," | ||
244 | "directories_added INTEGER," | ||
245 | "directories_removed INTEGER," | ||
246 | "directories_changed INTEGER," | ||
247 | "symlinks_added INTEGER," | ||
248 | "symlinks_removed INTEGER," | ||
249 | "symlinks_changed INTEGER," | ||
250 | "sz_bytes_added INTEGER," | ||
251 | "sz_bytes_removed INTEGER," | ||
252 | "sz_bytes_uploaded INTEGER," | ||
253 | "sz_catalog_bytes_uploaded INTEGER," | ||
254 | "success INTEGER);") | ||
255 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | .Execute(); |
256 | const bool ret2 = | ||
257 |
3/6✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
|
56 | sqlite::Sql(sqlite_db(), "CREATE TABLE gc_statistics (" |
258 | "gc_id INTEGER PRIMARY KEY," | ||
259 | "start_time TEXT," | ||
260 | "finish_time TEXT," | ||
261 | "n_preserved_catalogs INTEGER," | ||
262 | "n_condemned_catalogs INTEGER," | ||
263 | "n_condemned_objects INTEGER," | ||
264 | "sz_condemned_bytes INTEGER," | ||
265 | "n_duplicate_delete_requests INTEGER," | ||
266 | "success INTEGER);") | ||
267 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | .Execute(); |
268 | 28 | return ret1 & ret2; | |
269 | } | ||
270 | |||
271 | |||
272 | 28 | bool StatisticsDatabase::CheckSchemaCompatibility() { | |
273 | 28 | ++check_compatibility_calls; | |
274 | 28 | return (schema_version() > kLatestCompatibleSchema - 0.1 | |
275 |
2/4✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
|
28 | && schema_version() < kLatestCompatibleSchema + 0.1); |
276 | } | ||
277 | |||
278 | |||
279 | 28 | bool StatisticsDatabase::LiveSchemaUpgradeIfNecessary() { | |
280 | 28 | ++live_upgrade_calls; | |
281 | 28 | if (IsEqualSchema(schema_version(), kLatestSchema) | |
282 |
5/6✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 14 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 14 times.
|
28 | && (schema_revision() == 1)) { |
283 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | LogCvmfs(kLogCvmfs, kLogDebug, |
284 | "upgrading schema revision (1 --> 2) of " | ||
285 | "statistics database"); | ||
286 | |||
287 | sqlite::Sql publish_upgrade2_1( | ||
288 | this->sqlite_db(), | ||
289 | "ALTER TABLE " | ||
290 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics RENAME COLUMN finished_time TO finish_time;"); |
291 | sqlite::Sql publish_upgrade2_2(this->sqlite_db(), | ||
292 | "ALTER TABLE " | ||
293 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD revision INTEGER;"); |
294 | sqlite::Sql publish_upgrade2_3(this->sqlite_db(), | ||
295 | "ALTER TABLE " | ||
296 | "publish_statistics RENAME COLUMN " | ||
297 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "duplicated_files TO chunks_duplicated;"); |
298 | sqlite::Sql publish_upgrade2_4( | ||
299 | this->sqlite_db(), "ALTER TABLE " | ||
300 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD chunks_added INTEGER;"); |
301 | sqlite::Sql publish_upgrade2_5( | ||
302 | this->sqlite_db(), "ALTER TABLE " | ||
303 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD symlinks_added INTEGER;"); |
304 | sqlite::Sql publish_upgrade2_6( | ||
305 | this->sqlite_db(), "ALTER TABLE " | ||
306 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD symlinks_removed INTEGER;"); |
307 | sqlite::Sql publish_upgrade2_7( | ||
308 | this->sqlite_db(), "ALTER TABLE " | ||
309 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD symlinks_changed INTEGER;"); |
310 | sqlite::Sql publish_upgrade2_8( | ||
311 | this->sqlite_db(), "ALTER TABLE " | ||
312 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD catalogs_added INTEGER;"); |
313 | sqlite::Sql publish_upgrade2_9( | ||
314 | this->sqlite_db(), | ||
315 | "ALTER TABLE " | ||
316 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | "publish_statistics ADD sz_catalog_bytes_uploaded INTEGER;"); |
317 | |||
318 |
3/7✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
|
28 | if (!publish_upgrade2_1.Execute() || !publish_upgrade2_2.Execute() |
319 |
4/9✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
14 | || !publish_upgrade2_3.Execute() || !publish_upgrade2_4.Execute() |
320 |
4/9✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
14 | || !publish_upgrade2_5.Execute() || !publish_upgrade2_6.Execute() |
321 |
4/9✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
14 | || !publish_upgrade2_7.Execute() || !publish_upgrade2_8.Execute() |
322 |
4/8✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
|
28 | || !publish_upgrade2_9.Execute()) { |
323 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
324 | "failed to upgrade publish_statistics" | ||
325 | " table of statistics database"); | ||
326 | ✗ | return false; | |
327 | } | ||
328 | |||
329 | sqlite::Sql gc_upgrade2_1(this->sqlite_db(), | ||
330 | "ALTER TABLE gc_statistics" | ||
331 |
3/6✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 14 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 14 times.
✗ Branch 9 not taken.
|
28 | " RENAME COLUMN finished_time TO finish_time;"); |
332 | |||
333 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
|
14 | if (!gc_upgrade2_1.Execute()) { |
334 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
335 | "failed to upgrade gc_statistics" | ||
336 | " table of statistics database"); | ||
337 | ✗ | return false; | |
338 | } | ||
339 | |||
340 | 14 | set_schema_revision(2); | |
341 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
|
14 | if (!StoreSchemaRevision()) { |
342 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
343 | "failed to upgrade schema revision" | ||
344 | " of statistics database"); | ||
345 | ✗ | return false; | |
346 | } | ||
347 |
10/20✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 14 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 14 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 14 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 14 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 14 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 14 times.
✗ Branch 29 not taken.
|
14 | } |
348 | |||
349 | 28 | if (IsEqualSchema(schema_version(), kLatestSchema) | |
350 |
3/6✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
|
28 | && (schema_revision() == 2)) { |
351 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | LogCvmfs(kLogCvmfs, kLogDebug, |
352 | "upgrading schema revision (2 --> 3) of " | ||
353 | "statistics database"); | ||
354 | |||
355 | sqlite::Sql publish_upgrade3_1(this->sqlite_db(), | ||
356 | "ALTER TABLE " | ||
357 |
3/6✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
|
56 | "publish_statistics ADD success INTEGER;"); |
358 | |||
359 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
|
28 | if (!publish_upgrade3_1.Execute()) { |
360 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
361 | "failed to upgrade publish_statistics" | ||
362 | " table of statistics database"); | ||
363 | ✗ | return false; | |
364 | } | ||
365 | |||
366 | sqlite::Sql gc_upgrade3_1(this->sqlite_db(), "ALTER TABLE gc_statistics" | ||
367 |
3/6✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
|
56 | " ADD success INTEGER;"); |
368 | |||
369 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
|
28 | if (!gc_upgrade3_1.Execute()) { |
370 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
371 | "failed to upgrade gc_statistics" | ||
372 | " table of statistics database"); | ||
373 | ✗ | return false; | |
374 | } | ||
375 | |||
376 | 28 | set_schema_revision(3); | |
377 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
|
28 | if (!StoreSchemaRevision()) { |
378 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
379 | "failed to upgrade schema revision" | ||
380 | " of statistics database"); | ||
381 | ✗ | return false; | |
382 | } | ||
383 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28 times.
✗ Branch 5 not taken.
|
28 | } |
384 | |||
385 | 28 | if (IsEqualSchema(schema_version(), kLatestSchema) | |
386 |
3/6✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
|
28 | && (schema_revision() == 3)) { |
387 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | LogCvmfs(kLogCvmfs, kLogDebug, |
388 | "upgrading schema revision (3 --> 4) of " | ||
389 | "statistics database"); | ||
390 | |||
391 | sqlite::Sql gc_upgrade4_1(this->sqlite_db(), | ||
392 | "ALTER TABLE gc_statistics" | ||
393 |
3/6✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
|
56 | " ADD n_duplicate_delete_requests INTEGER;"); |
394 | |||
395 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
|
28 | if (!gc_upgrade4_1.Execute()) { |
396 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
397 | "failed to upgrade gc_statistics " | ||
398 | "table of statistics database"); | ||
399 | ✗ | return false; | |
400 | } | ||
401 | |||
402 | 28 | set_schema_revision(4); | |
403 |
2/4✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 28 times.
|
28 | if (!StoreSchemaRevision()) { |
404 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
405 | "failed to upgrade schema revision " | ||
406 | "of statistics database"); | ||
407 | ✗ | return false; | |
408 | } | ||
409 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | } |
410 | 28 | return true; | |
411 | } | ||
412 | |||
413 | |||
414 | ✗ | bool StatisticsDatabase::CompactDatabase() const { | |
415 | ✗ | ++compact_calls; | |
416 | ✗ | return !compacting_fails; | |
417 | } | ||
418 | |||
419 | |||
420 | 56 | StatisticsDatabase::~StatisticsDatabase() { --StatisticsDatabase::instances; } | |
421 | |||
422 | |||
423 | ✗ | StatisticsDatabase *StatisticsDatabase::OpenStandardDB( | |
424 | const std::string repo_name) { | ||
425 | StatisticsDatabase *db; | ||
426 | ✗ | std::string db_file_path; | |
427 | uint32_t days_to_keep; | ||
428 | ✗ | GetDBParams(repo_name, &db_file_path, &days_to_keep); | |
429 | ✗ | if (FileExists(db_file_path)) { | |
430 | ✗ | db = StatisticsDatabase::Open(db_file_path, kOpenReadWrite); | |
431 | ✗ | if (db == NULL) { | |
432 | ✗ | PANIC(kLogSyslogErr, "Couldn't create StatisticsDatabase object!"); | |
433 | ✗ | } else if (db->GetProperty<std::string>("repo_name") != repo_name) { | |
434 | ✗ | PANIC(kLogSyslogErr, | |
435 | "'repo_name' property of the statistics database %s " | ||
436 | "is incorrect. Please fix the database.", | ||
437 | db_file_path.c_str()); | ||
438 | } | ||
439 | ✗ | if (!db->Prune(days_to_keep)) { | |
440 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, "Failed to prune statistics database"); | |
441 | } | ||
442 | } else { | ||
443 | ✗ | db = StatisticsDatabase::Create(db_file_path); | |
444 | ✗ | if (db == NULL) { | |
445 | ✗ | PANIC(kLogSyslogErr, "Couldn't create StatisticsDatabase object!"); | |
446 | // insert repo_name into properties table | ||
447 | ✗ | } else if (!db->SetProperty("repo_name", repo_name)) { | |
448 | ✗ | PANIC(kLogSyslogErr, "Couldn't insert repo_name into properties table!"); | |
449 | } | ||
450 | } | ||
451 | ✗ | db->repo_name_ = repo_name; | |
452 | ✗ | return db; | |
453 | } | ||
454 | |||
455 | |||
456 | ✗ | bool StatisticsDatabase::StorePublishStatistics( | |
457 | const perf::Statistics *statistics, | ||
458 | const std::string &start_time, | ||
459 | const bool success) { | ||
460 | ✗ | const std::string finish_time = GetGMTimestamp(); | |
461 | const std::string statement = | ||
462 | ✗ | PrepareStatementIntoPublish(statistics, start_time, finish_time, success); | |
463 | ✗ | return StoreEntry(statement); | |
464 | } | ||
465 | |||
466 | |||
467 | ✗ | bool StatisticsDatabase::StoreGCStatistics(const perf::Statistics *statistics, | |
468 | const std::string &start_time, | ||
469 | const bool success) { | ||
470 | ✗ | const std::string finish_time = GetGMTimestamp(); | |
471 | const std::string statement = PrepareStatementIntoGc( | ||
472 | ✗ | statistics, start_time, finish_time, repo_name_, success); | |
473 | ✗ | return StoreEntry(statement); | |
474 | } | ||
475 | |||
476 | |||
477 | ✗ | bool StatisticsDatabase::StoreEntry(const std::string &insert_statement) { | |
478 | ✗ | sqlite::Sql insert(this->sqlite_db(), insert_statement); | |
479 | |||
480 | ✗ | if (!insert.Execute()) { | |
481 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
482 | "Couldn't store statistics in %s: insert.Execute failed!", | ||
483 | ✗ | this->filename().c_str()); | |
484 | ✗ | return false; | |
485 | } | ||
486 | |||
487 | ✗ | LogCvmfs(kLogCvmfs, kLogStdout, "Statistics stored at: %s", | |
488 | ✗ | this->filename().c_str()); | |
489 | ✗ | return true; | |
490 | } | ||
491 | |||
492 | |||
493 | ✗ | bool StatisticsDatabase::Prune(uint32_t days) { | |
494 | ✗ | if (days == 0) | |
495 | ✗ | return true; | |
496 | |||
497 | const std::string publish_stmt = | ||
498 | "DELETE FROM publish_statistics WHERE " | ||
499 | ✗ | "julianday('now','start of day')-julianday(start_time) > " + | |
500 | ✗ | StringifyUint(days) + ";"; | |
501 | |||
502 | const std::string gc_stmt = | ||
503 | "DELETE FROM gc_statistics WHERE " | ||
504 | ✗ | "julianday('now','start of day')-julianday(start_time) > " + | |
505 | ✗ | StringifyUint(days) + ";"; | |
506 | |||
507 | ✗ | sqlite::Sql publish_sql(this->sqlite_db(), publish_stmt); | |
508 | ✗ | sqlite::Sql gc_sql(this->sqlite_db(), gc_stmt); | |
509 | ✗ | if (!publish_sql.Execute() || !gc_sql.Execute()) { | |
510 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
511 | "Couldn't prune statistics DB %s: SQL Execute() failed!", | ||
512 | ✗ | this->filename().c_str()); | |
513 | ✗ | return false; | |
514 | } | ||
515 | ✗ | if (!this->Vacuum()) { | |
516 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
517 | "Couldn't prune statistics DB %s: Vacuum() failed!", | ||
518 | ✗ | this->filename().c_str()); | |
519 | ✗ | return false; | |
520 | } | ||
521 | |||
522 | ✗ | return true; | |
523 | } | ||
524 | |||
525 | |||
526 | ✗ | bool StatisticsDatabase::UploadStatistics(upload::Spooler *spooler, | |
527 | std::string local_path) { | ||
528 | ✗ | if (local_path == "") { | |
529 | ✗ | local_path = this->filename(); | |
530 | } | ||
531 | |||
532 | ✗ | spooler->WaitForUpload(); | |
533 | ✗ | const unsigned errors_before = spooler->GetNumberOfErrors(); | |
534 | ✗ | spooler->Mkdir("stats"); | |
535 | ✗ | spooler->RemoveAsync("stats/stats.db"); | |
536 | ✗ | spooler->WaitForUpload(); | |
537 | ✗ | spooler->Upload(local_path, "stats/stats.db"); | |
538 | ✗ | spooler->WaitForUpload(); | |
539 | ✗ | const unsigned errors_after = spooler->GetNumberOfErrors(); | |
540 | |||
541 | ✗ | if (errors_before != errors_after) { | |
542 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
543 | "Could not upload statistics DB file into storage backend"); | ||
544 | ✗ | return false; | |
545 | } | ||
546 | ✗ | return true; | |
547 | } | ||
548 | |||
549 | |||
550 | ✗ | bool StatisticsDatabase::UploadStatistics(upload::AbstractUploader *uploader, | |
551 | std::string local_path) { | ||
552 | ✗ | if (local_path == "") { | |
553 | ✗ | local_path = this->filename(); | |
554 | } | ||
555 | |||
556 | ✗ | uploader->WaitForUpload(); | |
557 | ✗ | const unsigned errors_before = uploader->GetNumberOfErrors(); | |
558 | |||
559 | ✗ | uploader->RemoveAsync("stats/stats.db"); | |
560 | ✗ | uploader->WaitForUpload(); | |
561 | ✗ | uploader->UploadFile(local_path, "stats/stats.db"); | |
562 | ✗ | uploader->WaitForUpload(); | |
563 | ✗ | const unsigned errors_after = uploader->GetNumberOfErrors(); | |
564 | ✗ | return errors_before == errors_after; | |
565 | } | ||
566 | |||
567 | |||
568 | ✗ | void StatisticsDatabase::GetDBParams(const std::string &repo_name, | |
569 | std::string *path, | ||
570 | uint32_t *days_to_keep) { | ||
571 | // default location | ||
572 | ✗ | const std::string db_default_path = "/var/spool/cvmfs/" + repo_name | |
573 | ✗ | + "/stats.db"; | |
574 | ✗ | const std::string repo_config_file = "/etc/cvmfs/repositories.d/" + repo_name | |
575 | ✗ | + "/server.conf"; | |
576 | ✗ | SimpleOptionsParser parser; | |
577 | |||
578 | ✗ | if (!parser.TryParsePath(repo_config_file)) { | |
579 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
580 | "Could not parse repository configuration: %s.", | ||
581 | repo_config_file.c_str()); | ||
582 | ✗ | *path = db_default_path; | |
583 | ✗ | *days_to_keep = kDefaultDaysToKeep; | |
584 | ✗ | return; | |
585 | } | ||
586 | |||
587 | ✗ | std::string statistics_db = ""; | |
588 | ✗ | if (!parser.GetValue("CVMFS_STATISTICS_DB", &statistics_db)) { | |
589 | ✗ | LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, | |
590 | "Parameter %s was not set in the repository configuration file. " | ||
591 | "Using default value: %s", | ||
592 | "CVMFS_STATISTICS_DB", db_default_path.c_str()); | ||
593 | ✗ | *path = db_default_path; | |
594 | } else { | ||
595 | ✗ | const std::string dirname = GetParentPath(statistics_db); | |
596 | ✗ | const int mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | | |
597 | S_IXOTH; // 755 | ||
598 | ✗ | if (!MkdirDeep(dirname, mode, true)) { | |
599 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
600 | "Couldn't write statistics at the specified path %s.", | ||
601 | statistics_db.c_str()); | ||
602 | ✗ | *path = db_default_path; | |
603 | } else { | ||
604 | ✗ | *path = statistics_db; | |
605 | } | ||
606 | } | ||
607 | |||
608 | ✗ | const std::string days_to_keep_str = ""; | |
609 | ✗ | if (!parser.GetValue("CVMFS_STATS_DB_DAYS_TO_KEEP", &statistics_db)) { | |
610 | ✗ | LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, | |
611 | "Parameter %s was not set in the repository configuration file. " | ||
612 | "Using default value: %s", | ||
613 | "CVMFS_STATS_DB_DAYS_TO_KEEP", | ||
614 | ✗ | StringifyUint(kDefaultDaysToKeep).c_str()); | |
615 | ✗ | *days_to_keep = kDefaultDaysToKeep; | |
616 | } else { | ||
617 | ✗ | *days_to_keep = static_cast<uint32_t>(String2Uint64(days_to_keep_str)); | |
618 | } | ||
619 | } | ||
620 | |||
621 | |||
622 | /** | ||
623 | * Check if the CVMFS_EXTENDED_GC_STATS is ON or not | ||
624 | * | ||
625 | * @param repo_name fully qualified name of the repository | ||
626 | * @return true if CVMFS_EXTENDED_GC_STATS is ON | ||
627 | */ | ||
628 | ✗ | bool StatisticsDatabase::GcExtendedStats(const std::string &repo_name) { | |
629 | ✗ | SimpleOptionsParser parser; | |
630 | ✗ | std::string param_value = ""; | |
631 | ✗ | const std::string repo_config_file = "/etc/cvmfs/repositories.d/" + repo_name | |
632 | ✗ | + "/server.conf"; | |
633 | |||
634 | ✗ | if (!parser.TryParsePath(repo_config_file)) { | |
635 | ✗ | LogCvmfs(kLogCvmfs, kLogSyslogErr, | |
636 | "Could not parse repository configuration: %s.", | ||
637 | repo_config_file.c_str()); | ||
638 | ✗ | return false; | |
639 | } | ||
640 | ✗ | if (!parser.GetValue("CVMFS_EXTENDED_GC_STATS", ¶m_value)) { | |
641 | ✗ | LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslog, | |
642 | "Parameter %s was not set in the repository configuration file. " | ||
643 | "condemned_bytes were not counted.", | ||
644 | "CVMFS_EXTENDED_GC_STATS"); | ||
645 | ✗ | } else if (parser.IsOn(param_value)) { | |
646 | ✗ | return true; | |
647 | } | ||
648 | ✗ | return false; | |
649 | } | ||
650 | |||
651 | |||
652 | 56 | StatisticsDatabase::StatisticsDatabase(const std::string &filename, | |
653 | 56 | const OpenMode open_mode) | |
654 | : sqlite::Database<StatisticsDatabase>(filename, open_mode) | ||
655 | 56 | , create_empty_db_calls(0) | |
656 | 56 | , check_compatibility_calls(0) | |
657 | 56 | , live_upgrade_calls(0) | |
658 | 56 | , compact_calls(0) { | |
659 | 56 | ++StatisticsDatabase::instances; | |
660 | 56 | } | |
661 | |||
662 | const uint32_t StatisticsDatabase::kDefaultDaysToKeep = 365; | ||
663 |