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