• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1661

11 Sep 2023 11:58PM UTC coverage: 91.265% (+0.03%) from 91.236%
1661

push

Evergreen

GitHub
make DB::get_number_of_versions() reflect the number of live versions (#6960)

95858 of 175764 branches covered (0.0%)

21 of 21 new or added lines in 2 files covered. (100.0%)

80 existing lines in 16 files now uncovered.

233621 of 255982 relevant lines covered (91.26%)

7117774.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.8
/test/test_metrics.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include "testsettings.hpp"
20

21
#if defined(TEST_METRICS)
22
#include "test.hpp"
23

24

25
// Test independence and thread-safety
26
// -----------------------------------
27
//
28
// All tests must be thread safe and independent of each other. This
29
// is required because it allows for both shuffling of the execution
30
// order and for parallelized testing.
31
//
32
// In particular, avoid using std::rand() since it is not guaranteed
33
// to be thread safe. Instead use the API offered in
34
// `test/util/random.hpp`.
35
//
36
// All files created in tests must use the TEST_PATH macro (or one of
37
// its friends) to obtain a suitable file system path. See
38
// `test/util/test_path.hpp`.
39
//
40
//
41
// Debugging and the ONLY() macro
42
// ------------------------------
43
//
44
// A simple way of disabling all tests except one called `Foo`, is to
45
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
46
// test suite. Note that you can also use filtering by setting the
47
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
48
// this.
49
//
50
// Another way to debug a particular test, is to copy that test into
51
// `experiments/testcase.cpp` and then run `sh build.sh
52
// check-testcase` (or one of its friends) from the command line.
53

54
#include <realm.hpp>
55
#include <realm/replication.hpp>
56

57
using namespace realm;
58
using namespace realm::metrics;
59
using namespace realm::test_util;
60
using namespace realm::util;
61

62
#if REALM_METRICS
63

64
#include <realm/util/encrypted_file_mapping.hpp>
65
#include <realm/util/to_string.hpp>
66
#include <realm/util/scope_exit.hpp>
67

68
#include <future>
69
#include <chrono>
70
#include <string>
71
#include <thread>
72
#include <vector>
73

74

75
TEST(Metrics_HasNoReportsWhenDisabled)
76
{
2✔
77
    SHARED_GROUP_TEST_PATH(path);
2✔
78
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
79
    DBOptions options(crypt_key());
2✔
80
    options.enable_metrics = false;
2✔
81
    DBRef sg = DB::create(*hist, path, options);
2✔
82
    CHECK(!sg->get_metrics());
2✔
83
    auto wt = sg->start_write();
2✔
84
    auto table = wt->add_table("table");
2✔
85
    auto col = table->add_column(type_Int, "first");
2✔
86
    std::vector<ObjKey> keys;
2✔
87
    table->create_objects(10, keys);
2✔
88
    wt->commit();
2✔
89
    auto rt = sg->start_read();
2✔
90
    table = rt->get_table("table");
2✔
91
    CHECK(bool(table));
2✔
92
    Query query = table->column<int64_t>(col) == 0;
2✔
93
    query.count();
2✔
94
    rt->end_read();
2✔
95
    CHECK(!sg->get_metrics());
2✔
96
}
2✔
97

98
TEST(Metrics_HasReportsWhenEnabled)
99
{
2✔
100
    SHARED_GROUP_TEST_PATH(path);
2✔
101
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
102
    DBOptions options(crypt_key());
2✔
103
    options.enable_metrics = true;
2✔
104
    DBRef sg = DB::create(*hist, path, options);
2✔
105
    CHECK(sg->get_metrics());
2✔
106
    auto wt = sg->start_write();
2✔
107
    auto table = wt->add_table("table");
2✔
108
    auto col = table->add_column(type_Int, "first");
2✔
109
    std::vector<ObjKey> keys;
2✔
110
    table->create_objects(10, keys);
2✔
111
    wt->commit();
2✔
112
    auto rt = sg->start_read();
2✔
113
    table = rt->get_table("table");
2✔
114
    CHECK(bool(table));
2✔
115
    Query query = table->column<int64_t>(col) == 0;
2✔
116
    query.count();
2✔
117
    rt->end_read();
2✔
118
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
119
    CHECK(metrics);
2✔
120
    CHECK(metrics->num_query_metrics() != 0);
2✔
121
    CHECK(metrics->num_transaction_metrics() != 0);
2✔
122
}
2✔
123

124
TEST(Metrics_QueryTypes)
125
{
2✔
126
    SHARED_GROUP_TEST_PATH(path);
2✔
127
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
128
    DBOptions options(crypt_key());
2✔
129
    options.enable_metrics = true;
2✔
130
    DBRef sg = DB::create(*hist, path, options);
2✔
131
    CHECK(sg->get_metrics());
2✔
132
    auto wt = sg->start_write();
2✔
133
    auto table = wt->add_table("table");
2✔
134
    auto int_col = table->add_column(type_Int, "col_int");
2✔
135
    auto double_col = table->add_column(type_Double, "col_double");
2✔
136
    auto float_col = table->add_column(type_Float, "col_float");
2✔
137
    auto timestamp_col = table->add_column(type_Timestamp, "col_timestamp");
2✔
138
    std::vector<ObjKey> keys;
2✔
139
    table->create_objects(10, keys);
2✔
140
    wt->commit();
2✔
141
    auto rt = sg->start_read();
2✔
142
    table = rt->get_table("table");
2✔
143
    CHECK(bool(table));
2✔
144
    Query query = table->column<int64_t>(int_col) == 0;
2✔
145
    query.find();
2✔
146
    query.find_all();
2✔
147
    query.count();
2✔
148
    query.sum(int_col);
2✔
149
    query.avg(int_col);
2✔
150
    query.max(int_col);
2✔
151
    query.min(int_col);
2✔
152

1✔
153
    query.sum(double_col);
2✔
154
    query.avg(double_col);
2✔
155
    query.max(double_col);
2✔
156
    query.min(double_col);
2✔
157

1✔
158
    query.sum(float_col);
2✔
159
    query.avg(float_col);
2✔
160
    query.max(float_col);
2✔
161
    query.min(float_col);
2✔
162

1✔
163
    ObjKey return_dummy;
2✔
164
    query.max(timestamp_col, &return_dummy);
2✔
165
    query.min(timestamp_col, &return_dummy);
2✔
166

1✔
167
    rt->end_read();
2✔
168
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
169
    CHECK(metrics);
2✔
170
    CHECK_EQUAL(metrics->num_query_metrics(), 17);
2✔
171
    std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
2✔
172
    CHECK_EQUAL(metrics->num_query_metrics(), 0);
2✔
173
    CHECK(queries);
2✔
174
    CHECK_EQUAL(queries->size(), 17);
2✔
175
    CHECK_EQUAL(queries->at(0).get_type(), QueryInfo::type_Find);
2✔
176
    CHECK_EQUAL(queries->at(1).get_type(), QueryInfo::type_FindAll);
2✔
177
    CHECK_EQUAL(queries->at(2).get_type(), QueryInfo::type_Count);
2✔
178
    CHECK_EQUAL(queries->at(3).get_type(), QueryInfo::type_Sum);
2✔
179
    CHECK_EQUAL(queries->at(4).get_type(), QueryInfo::type_Average);
2✔
180
    CHECK_EQUAL(queries->at(5).get_type(), QueryInfo::type_Maximum);
2✔
181
    CHECK_EQUAL(queries->at(6).get_type(), QueryInfo::type_Minimum);
2✔
182

1✔
183
    CHECK_EQUAL(queries->at(7).get_type(), QueryInfo::type_Sum);
2✔
184
    CHECK_EQUAL(queries->at(8).get_type(), QueryInfo::type_Average);
2✔
185
    CHECK_EQUAL(queries->at(9).get_type(), QueryInfo::type_Maximum);
2✔
186
    CHECK_EQUAL(queries->at(10).get_type(), QueryInfo::type_Minimum);
2✔
187

1✔
188
    CHECK_EQUAL(queries->at(11).get_type(), QueryInfo::type_Sum);
2✔
189
    CHECK_EQUAL(queries->at(12).get_type(), QueryInfo::type_Average);
2✔
190
    CHECK_EQUAL(queries->at(13).get_type(), QueryInfo::type_Maximum);
2✔
191
    CHECK_EQUAL(queries->at(14).get_type(), QueryInfo::type_Minimum);
2✔
192

1✔
193
    CHECK_EQUAL(queries->at(15).get_type(), QueryInfo::type_Maximum);
2✔
194
    CHECK_EQUAL(queries->at(16).get_type(), QueryInfo::type_Minimum);
2✔
195
}
2✔
196

197
static size_t find_count(std::string haystack, std::string needle)
198
{
184✔
199
    size_t find_pos = 0;
184✔
200
    size_t count = 0;
184✔
201
    while (find_pos < haystack.size()) {
378✔
202
        find_pos = haystack.find(needle, find_pos);
378✔
203
        if (find_pos == std::string::npos)
378✔
204
            break;
184✔
205
        ++find_pos;
194✔
206
        ++count;
194✔
207
    }
194✔
208
    return count;
184✔
209
}
184✔
210

211
static void populate(DBRef sg)
212
{
16✔
213
    auto wt = sg->start_write();
16✔
214
    auto person = wt->add_table("person");
16✔
215
    auto pet = wt->add_table("pet");
16✔
216
    person->add_column(type_Int, "age");
16✔
217
    person->add_column(type_Double, "paid");
16✔
218
    person->add_column(type_Float, "weight");
16✔
219
    person->add_column(type_Timestamp, "date_of_birth");
16✔
220
    person->add_column(type_String, "name");
16✔
221
    person->add_column(type_Bool, "account_overdue");
16✔
222
    person->add_column(type_Binary, "data");
16✔
223
    auto owes_col = person->add_column_list(*person, "owes_coffee_to");
16✔
224

8✔
225
    auto create_person = [&](int age, double paid, float weight, Timestamp dob, std::string name, bool overdue,
16✔
226
                             std::string data, std::vector<ObjKey> owes_coffee_to) {
80✔
227
        BinaryData bd(data);
80✔
228
        Obj obj = person->create_object().set_all(age, paid, weight, dob, name, overdue, bd);
80✔
229
        auto ll = obj.get_linklist(owes_col);
80✔
230
        for (auto key : owes_coffee_to) {
176✔
231
            ll.add(key);
176✔
232
        }
176✔
233
        return obj.get_key();
80✔
234
    };
80✔
235

8✔
236
    auto k0 = create_person(27, 28.80, 170.7f, Timestamp(27, 5), "Bob", true, "e72s", {});
16✔
237
    auto k1 = create_person(28, 10.70, 165.8f, Timestamp(28, 8), "Ryan", false, "s83f", {k0});
16✔
238
    auto k2 = create_person(33, 55.28, 183.3f, Timestamp(33, 3), "Cole", true, "s822k", {k1, k0});
16✔
239
    auto k3 = create_person(39, 22.72, 173.8f, Timestamp(39, 2), "Nathan", true, "h282l", {k1, k1, k0, k2});
16✔
240
    create_person(33, 29.28, 188.7f, Timestamp(33, 9), "Riley", false, "a208s", {k3, k3, k2, k1});
16✔
241

8✔
242
    pet->add_column(type_String, "name");
16✔
243
    pet->add_column(*person, "owner");
16✔
244

8✔
245
    auto create_pet = [&](std::string name, ObjKey owner) {
96✔
246
        pet->create_object().set_all(name, owner);
96✔
247
    };
96✔
248

8✔
249
    create_pet("Fido", k0);
16✔
250
    create_pet("Max", k1);
16✔
251
    create_pet("Buddy", k2);
16✔
252
    create_pet("Rocky", k3);
16✔
253
    create_pet("Toby", k3);
16✔
254
    create_pet("Duke", k0);
16✔
255

8✔
256
    wt->commit();
16✔
257
}
16✔
258

259
TEST(Metrics_QueryEqual)
260
{
2✔
261
    SHARED_GROUP_TEST_PATH(path);
2✔
262
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
263
    DBOptions options(crypt_key());
2✔
264
    options.enable_metrics = true;
2✔
265
    DBRef sg = DB::create(*hist, path, options);
2✔
266
    populate(sg);
2✔
267

1✔
268
    std::string person_table_name = "person";
2✔
269
    std::string pet_table_name = "pet";
2✔
270
    std::string query_search_term = "==";
2✔
271

1✔
272
    auto wt = sg->start_write();
2✔
273
    TableRef person = wt->get_table(person_table_name);
2✔
274
    TableRef pet = wt->get_table(pet_table_name);
2✔
275
    CHECK(bool(person));
2✔
276

1✔
277
    CHECK_EQUAL(person->get_column_count(), 8);
2✔
278
    std::vector<std::string> column_names;
2✔
279
    for (auto col : person->get_column_keys()) {
16✔
280
        column_names.push_back(person->get_column_name(col));
16✔
281
    }
16✔
282
    for (auto col : pet->get_column_keys()) {
4✔
283
        column_names.push_back(pet->get_column_name(col));
4✔
284
    }
4✔
285

1✔
286
    Obj p0 = person->get_object(0);
2✔
287

1✔
288
    auto col_age = person->get_column_key("age");
2✔
289
    auto col_paid = person->get_column_key("paid");
2✔
290
    auto col_weight = person->get_column_key("weight");
2✔
291
    auto col_birth = person->get_column_key("date_of_birth");
2✔
292
    auto col_name = person->get_column_key("name");
2✔
293
    auto col_overdue = person->get_column_key("account_overdue");
2✔
294
    auto col_data = person->get_column_key("data");
2✔
295
    auto col_owes = person->get_column_key("owes_coffee_to");
2✔
296

1✔
297
    auto col_pet_name = pet->get_column_key("name");
2✔
298
    auto col_owner = pet->get_column_key("owner");
2✔
299

1✔
300
    Query q0 = person->column<int64_t>(col_age) == 0;
2✔
301
    Query q1 = person->column<double>(col_paid) == 0.0;
2✔
302
    Query q2 = person->column<float>(col_weight) == 0.0f;
2✔
303
    Query q3 = person->column<Timestamp>(col_birth) == Timestamp(0, 0);
2✔
304
    StringData name("");
2✔
305
    Query q4 = person->column<StringData>(col_name) == name;
2✔
306
    Query q5 = person->column<bool>(col_overdue) == false;
2✔
307
    BinaryData bd("");
2✔
308
    Query q6 = person->column<BinaryData>(col_data) == bd;
2✔
309
    Query q7 = person->column<Link>(col_owes) == p0;
2✔
310
    Query q8 = pet->column<String>(col_pet_name) == name;
2✔
311
    Query q9 = pet->column<Link>(col_owner) == p0;
2✔
312

1✔
313
    q0.find_all();
2✔
314
    q1.find_all();
2✔
315
    q2.find_all();
2✔
316
    q3.find_all();
2✔
317
    q4.find_all();
2✔
318
    q5.find_all();
2✔
319
    q6.find_all();
2✔
320
    q7.find_all();
2✔
321
    q8.find_all();
2✔
322
    q9.find_all();
2✔
323

1✔
324
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
325
    CHECK(metrics);
2✔
326
    std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
2✔
327
    CHECK(queries);
2✔
328
    CHECK_EQUAL(queries->size(), 10);
2✔
329

1✔
330
    for (size_t i = 0; i < 10; ++i) {
22✔
331
        std::string description = queries->at(i).get_description();
20✔
332
        std::string table_name = queries->at(i).get_table_name();
20✔
333
        CHECK_EQUAL(find_count(description, column_names[i]), 1);
20✔
334
        CHECK_GREATER_EQUAL(find_count(description, query_search_term), 1);
20✔
335
        if (i < 8) {
20✔
336
            CHECK_EQUAL(table_name, "person");
16✔
337
        }
16✔
338
        else {
4✔
339
            CHECK_EQUAL(table_name, "pet");
4✔
340
        }
4✔
341
    }
20✔
342
}
2✔
343

344
TEST(Metrics_QueryOrAndNot)
345
{
2✔
346
    SHARED_GROUP_TEST_PATH(path);
2✔
347
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
348
    DBOptions options(crypt_key());
2✔
349
    options.enable_metrics = true;
2✔
350
    DBRef sg = DB::create(*hist, path, options);
2✔
351
    populate(sg);
2✔
352

1✔
353
    std::string person_table_name = "person";
2✔
354
    std::string pet_table_name = "pet";
2✔
355
    std::string query_search_term = "==";
2✔
356
    std::string not_text = "!";
2✔
357

1✔
358
    auto wt = sg->start_write();
2✔
359
    TableRef person = wt->get_table(person_table_name);
2✔
360
    wt->get_table(pet_table_name);
2✔
361
    CHECK(bool(person));
2✔
362

1✔
363
    CHECK_EQUAL(person->get_column_count(), 8);
2✔
364
    std::vector<std::string> column_names;
2✔
365
    for (auto col : person->get_column_keys()) {
16✔
366
        column_names.push_back(person->get_column_name(col));
16✔
367
    }
16✔
368

1✔
369
    auto col_age = person->get_column_key("age");
2✔
370
    auto col_paid = person->get_column_key("paid");
2✔
371
    auto col_weight = person->get_column_key("weight");
2✔
372
    Query q0 = person->column<int64_t>(col_age) == 0;
2✔
373
    Query q1 = person->column<double>(col_paid) == 0.0;
2✔
374
    Query q2 = person->column<float>(col_weight) == 0.1f;
2✔
375

1✔
376
    Query simple_and = q0 && q1;
2✔
377
    Query simple_or = q0 || q1;
2✔
378
    Query simple_not = !q0;
2✔
379

1✔
380
    Query or_and = q2 || (simple_and);
2✔
381
    Query and_or = simple_and || q2;
2✔
382
    Query or_nested = q2 || simple_or;
2✔
383
    Query and_nested = q2 && simple_and;
2✔
384
    Query not_simple_and = !(simple_and);
2✔
385
    Query not_simple_or = !(simple_or);
2✔
386
    Query not_or_and = !(or_and);
2✔
387
    Query not_and_or = !(and_or);
2✔
388
    Query not_or_nested = !(or_nested);
2✔
389
    Query not_and_nested = !(and_nested);
2✔
390
    Query and_true = q0 && std::unique_ptr<realm::Expression>(new TrueExpression);
2✔
391
    Query and_false = q0 && std::unique_ptr<realm::Expression>(new FalseExpression);
2✔
392

1✔
393
    simple_and.find_all();
2✔
394
    simple_or.find_all();
2✔
395
    simple_not.find_all();
2✔
396
    or_and.find_all();
2✔
397
    and_or.find_all();
2✔
398
    or_nested.find_all();
2✔
399
    and_nested.find_all();
2✔
400
    not_simple_and.find_all();
2✔
401
    not_simple_or.find_all();
2✔
402
    not_or_and.find_all();
2✔
403
    not_and_or.find_all();
2✔
404
    not_or_nested.find_all();
2✔
405
    not_and_nested.find_all();
2✔
406
    and_true.find_all();
2✔
407
    and_false.find_all();
2✔
408

1✔
409
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
410
    CHECK(metrics);
2✔
411
    std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
2✔
412
    CHECK(queries);
2✔
413
    CHECK_EQUAL(queries->size(), 15);
2✔
414

1✔
415
    std::string and_description = queries->at(0).get_description();
2✔
416
    CHECK_EQUAL(find_count(and_description, " and "), 1);
2✔
417
    CHECK_EQUAL(find_count(and_description, column_names[0]), 1);
2✔
418
    CHECK_EQUAL(find_count(and_description, column_names[1]), 1);
2✔
419
    CHECK_EQUAL(find_count(and_description, query_search_term), 2);
2✔
420

1✔
421
    std::string or_description = queries->at(1).get_description();
2✔
422
    CHECK_EQUAL(find_count(or_description, " or "), 1);
2✔
423
    CHECK_EQUAL(find_count(or_description, column_names[0]), 1);
2✔
424
    CHECK_EQUAL(find_count(or_description, column_names[1]), 1);
2✔
425
    CHECK_EQUAL(find_count(or_description, query_search_term), 2);
2✔
426

1✔
427
    std::string not_description = queries->at(2).get_description();
2✔
428
    CHECK_EQUAL(find_count(not_description, not_text), 1);
2✔
429
    CHECK_EQUAL(find_count(not_description, column_names[0]), 1);
2✔
430
    CHECK_EQUAL(find_count(not_description, query_search_term), 1);
2✔
431

1✔
432
    std::string or_and_description = queries->at(3).get_description();
2✔
433
    CHECK_EQUAL(find_count(or_and_description, and_description), 1);
2✔
434
    CHECK_EQUAL(find_count(or_and_description, " or "), 1);
2✔
435
    CHECK_EQUAL(find_count(or_and_description, column_names[2]), 1);
2✔
436

1✔
437
    std::string and_or_description = queries->at(4).get_description();
2✔
438
    CHECK_EQUAL(find_count(and_or_description, and_description), 1);
2✔
439
    CHECK_EQUAL(find_count(and_or_description, " or "), 1);
2✔
440
    CHECK_EQUAL(find_count(and_or_description, column_names[2]), 1);
2✔
441

1✔
442
    std::string or_nested_description = queries->at(5).get_description();
2✔
443
    CHECK_EQUAL(find_count(or_nested_description, or_description), 1);
2✔
444
    CHECK_EQUAL(find_count(or_nested_description, " or "), 2);
2✔
445
    CHECK_EQUAL(find_count(or_nested_description, column_names[2]), 1);
2✔
446

1✔
447
    std::string and_nested_description = queries->at(6).get_description();
2✔
448
    CHECK_EQUAL(find_count(and_nested_description, and_description), 1);
2✔
449
    CHECK_EQUAL(find_count(and_nested_description, " and "), 2);
2✔
450
    CHECK_EQUAL(find_count(and_nested_description, column_names[2]), 1);
2✔
451

1✔
452
    std::string not_simple_and_description = queries->at(7).get_description();
2✔
453
    CHECK_EQUAL(find_count(not_simple_and_description, and_description), 1);
2✔
454
    CHECK_EQUAL(find_count(not_simple_and_description, not_text), 1);
2✔
455

1✔
456
    std::string not_simple_or_description = queries->at(8).get_description();
2✔
457
    CHECK_EQUAL(find_count(not_simple_or_description, or_description), 1);
2✔
458
    CHECK_EQUAL(find_count(not_simple_or_description, not_text), 1);
2✔
459

1✔
460
    std::string not_or_and_description = queries->at(9).get_description();
2✔
461
    CHECK_EQUAL(find_count(not_or_and_description, or_and_description), 1);
2✔
462
    CHECK_EQUAL(find_count(not_or_and_description, not_text), 1);
2✔
463

1✔
464
    std::string not_and_or_description = queries->at(10).get_description();
2✔
465
    CHECK_EQUAL(find_count(not_and_or_description, and_or_description), 1);
2✔
466
    CHECK_EQUAL(find_count(not_and_or_description, not_text), 1);
2✔
467

1✔
468
    std::string not_or_nested_description = queries->at(11).get_description();
2✔
469
    CHECK_EQUAL(find_count(not_or_nested_description, or_nested_description), 1);
2✔
470
    CHECK_EQUAL(find_count(not_or_nested_description, not_text), 1);
2✔
471

1✔
472
    std::string not_and_nested_description = queries->at(12).get_description();
2✔
473
    CHECK_EQUAL(find_count(not_and_nested_description, and_nested_description), 1);
2✔
474
    CHECK_EQUAL(find_count(not_and_nested_description, not_text), 1);
2✔
475

1✔
476
    std::string and_true_description = queries->at(13).get_description();
2✔
477
    CHECK_EQUAL(find_count(and_true_description, "and"), 1);
2✔
478
    CHECK_EQUAL(find_count(and_true_description, "TRUEPREDICATE"), 1);
2✔
479

1✔
480
    std::string and_false_description = queries->at(14).get_description();
2✔
481
    CHECK_EQUAL(find_count(and_false_description, "and"), 1);
2✔
482
    CHECK_EQUAL(find_count(and_false_description, "FALSEPREDICATE"), 1);
2✔
483
}
2✔
484

485

486
TEST(Metrics_LinkQueries)
487
{
2✔
488
    SHARED_GROUP_TEST_PATH(path);
2✔
489
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
490
    DBOptions options(crypt_key());
2✔
491
    options.enable_metrics = true;
2✔
492
    DBRef sg = DB::create(*hist, path, options);
2✔
493
    populate(sg);
2✔
494

1✔
495
    std::string person_table_name = "person";
2✔
496
    std::string pet_table_name = "pet";
2✔
497

1✔
498
    auto wt = sg->start_write();
2✔
499
    TableRef person = wt->get_table(person_table_name);
2✔
500
    TableRef pet = wt->get_table(pet_table_name);
2✔
501
    CHECK(bool(person));
2✔
502

1✔
503
    CHECK_EQUAL(person->get_column_count(), 8);
2✔
504
    std::vector<std::string> column_names;
2✔
505
    for (auto col : person->get_column_keys()) {
16✔
506
        column_names.push_back(person->get_column_name(col));
16✔
507
    }
16✔
508

1✔
509
    std::string pet_link_col_name = "owner";
2✔
510
    ColKey col_owner = pet->get_column_key(pet_link_col_name);
2✔
511
    auto col_age = person->get_column_key("age");
2✔
512

1✔
513
    Query q0 = pet->column<Link>(col_owner).is_null();
2✔
514
    Query q1 = pet->column<Link>(col_owner).is_not_null();
2✔
515
    Query q2 = pet->column<Link>(col_owner).count() == 1;
2✔
516
    Query q3 = pet->column<Link>(col_owner, person->column<int64_t>(col_age) >= 27).count() == 1;
2✔
517

1✔
518
    q0.find_all();
2✔
519
    q1.find_all();
2✔
520
    q2.find_all();
2✔
521
    q3.find_all();
2✔
522

1✔
523
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
524
    CHECK(metrics);
2✔
525
    std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
2✔
526
    CHECK(queries);
2✔
527

1✔
528
    CHECK_EQUAL(queries->size(), 4);
2✔
529

1✔
530
    std::string null_links_description = queries->at(0).get_description();
2✔
531
    CHECK_EQUAL(find_count(null_links_description, "NULL"), 1);
2✔
532
    CHECK_EQUAL(find_count(null_links_description, pet_link_col_name), 1);
2✔
533

1✔
534
    std::string not_null_links_description = queries->at(1).get_description();
2✔
535
    CHECK_EQUAL(find_count(not_null_links_description, "NULL"), 1);
2✔
536
    CHECK_EQUAL(find_count(not_null_links_description, "!"), 1);
2✔
537
    CHECK_EQUAL(find_count(not_null_links_description, pet_link_col_name), 1);
2✔
538

1✔
539
    std::string count_link_description = queries->at(2).get_description();
2✔
540
    CHECK_EQUAL(find_count(count_link_description, "@count"), 1);
2✔
541
    CHECK_EQUAL(find_count(count_link_description, pet_link_col_name), 1);
2✔
542
    CHECK_EQUAL(find_count(count_link_description, "=="), 1);
2✔
543

1✔
544
    std::string link_subquery_description = queries->at(3).get_description();
2✔
545
    CHECK_EQUAL(find_count(link_subquery_description, "@count"), 1);
2✔
546
    CHECK_EQUAL(find_count(link_subquery_description, pet_link_col_name), 1);
2✔
547
    CHECK_EQUAL(find_count(link_subquery_description, "=="), 1);
2✔
548
    CHECK_EQUAL(find_count(link_subquery_description, column_names[0]), 1);
2✔
549
    CHECK_EQUAL(find_count(link_subquery_description, ">"), 1);
2✔
550
}
2✔
551

552

553
TEST(Metrics_LinkListQueries)
554
{
2✔
555
    SHARED_GROUP_TEST_PATH(path);
2✔
556
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
557
    DBOptions options(crypt_key());
2✔
558
    options.enable_metrics = true;
2✔
559
    DBRef sg = DB::create(*hist, path, options);
2✔
560
    populate(sg);
2✔
561

1✔
562
    std::string person_table_name = "person";
2✔
563
    std::string pet_table_name = "pet";
2✔
564

1✔
565
    auto wt = sg->start_write();
2✔
566
    TableRef person = wt->get_table(person_table_name);
2✔
567
    wt->get_table(pet_table_name);
2✔
568
    CHECK(bool(person));
2✔
569

1✔
570
    CHECK_EQUAL(person->get_column_count(), 8);
2✔
571
    std::map<ColKey, std::string> column_names;
2✔
572
    for (auto col : person->get_column_keys()) {
16✔
573
        column_names[col] = person->get_column_name(col);
16✔
574
    }
16✔
575

1✔
576
    Obj p0 = person->get_object(0);
2✔
577

1✔
578
    ColKey ll_col_key = person->get_column_key("owes_coffee_to");
2✔
579
    auto col_name = person->get_column_key("name");
2✔
580
    auto col_paid = person->get_column_key("paid");
2✔
581

1✔
582
    Query q0 = person->column<Link>(ll_col_key).is_null();
2✔
583
    Query q1 = person->column<Link>(ll_col_key).is_not_null();
2✔
584
    Query q2 = person->column<Link>(ll_col_key).count() == 1;
2✔
585
    Query q3 = person->column<Link>(ll_col_key) == p0;
2✔
586
    Query q4 = person->column<Link>(ll_col_key).column<double>(col_paid).sum() >= 1;
2✔
587
    Query q5 = person->column<Link>(ll_col_key, person->column<String>(col_name) == "Bob").count() == 1;
2✔
588

1✔
589
    q0.find_all();
2✔
590
    q1.find_all();
2✔
591
    q2.find_all();
2✔
592
    q3.find_all();
2✔
593
    q4.find_all();
2✔
594
    q5.find_all();
2✔
595

1✔
596
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
597
    CHECK(metrics);
2✔
598
    std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
2✔
599
    CHECK(queries);
2✔
600

1✔
601
    CHECK_EQUAL(queries->size(), 6);
2✔
602

1✔
603
    std::string null_links_description = queries->at(0).get_description();
2✔
604
    CHECK_EQUAL(find_count(null_links_description, "NULL"), 1);
2✔
605
    CHECK_EQUAL(find_count(null_links_description, column_names[ll_col_key]), 1);
2✔
606

1✔
607
    std::string not_null_links_description = queries->at(1).get_description();
2✔
608
    CHECK_EQUAL(find_count(not_null_links_description, "NULL"), 1);
2✔
609
    CHECK_EQUAL(find_count(not_null_links_description, "!"), 1);
2✔
610
    CHECK_EQUAL(find_count(not_null_links_description, column_names[ll_col_key]), 1);
2✔
611

1✔
612
    std::string count_link_description = queries->at(2).get_description();
2✔
613
    CHECK_EQUAL(find_count(count_link_description, "@count"), 1);
2✔
614
    CHECK_EQUAL(find_count(count_link_description, column_names[ll_col_key]), 1);
2✔
615
    CHECK_EQUAL(find_count(count_link_description, "=="), 1);
2✔
616

1✔
617
    std::string links_description = queries->at(3).get_description();
2✔
618
    CHECK_EQUAL(find_count(links_description, "L0:0"), 1);
2✔
619
    CHECK_EQUAL(find_count(links_description, column_names[ll_col_key]), 1);
2✔
620
    CHECK_EQUAL(find_count(links_description, "=="), 1);
2✔
621

1✔
622
    std::string sum_link_description = queries->at(4).get_description();
2✔
623
    CHECK_EQUAL(find_count(sum_link_description, "@sum"), 1);
2✔
624
    CHECK_EQUAL(find_count(sum_link_description, column_names[ll_col_key]), 1);
2✔
625
    CHECK_EQUAL(find_count(sum_link_description, column_names[col_paid]), 1);
2✔
626
    // the query system can choose to flip the argument order and operators so that >= might be <=
1✔
627
    CHECK_EQUAL(find_count(sum_link_description, "<=") + find_count(sum_link_description, ">="), 1);
2✔
628

1✔
629
    std::string link_subquery_description = queries->at(5).get_description();
2✔
630
    CHECK_EQUAL(find_count(link_subquery_description, "@count"), 1);
2✔
631
    CHECK_EQUAL(find_count(link_subquery_description, column_names[ll_col_key]), 1);
2✔
632
    CHECK_EQUAL(find_count(link_subquery_description, "=="), 2);
2✔
633
    CHECK_EQUAL(find_count(link_subquery_description, column_names[col_name]), 1);
2✔
634
}
2✔
635

636

637
TEST(Metrics_SubQueries)
638
{
2✔
639
    SHARED_GROUP_TEST_PATH(path);
2✔
640
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
641
    DBOptions options(crypt_key());
2✔
642
    options.enable_metrics = true;
2✔
643
    DBRef sg = DB::create(*hist, path, options);
2✔
644

1✔
645
    auto wt = sg->start_write();
2✔
646

1✔
647
    std::string table_name = "table";
2✔
648
    std::string int_col_name = "integers";
2✔
649
    std::string str_col_name = "strings";
2✔
650

1✔
651
    TableRef table = wt->add_table(table_name);
2✔
652

1✔
653
    auto col_list_int = table->add_column_list(type_Int, int_col_name);
2✔
654
    auto col_list_string = table->add_column_list(type_String, str_col_name, true);
2✔
655
    auto col_other = table->add_column(type_String, "other");
2✔
656

1✔
657
    std::vector<ObjKey> keys;
2✔
658
    table->create_objects(4, keys);
2✔
659

1✔
660
    // see Query_SubtableExpression
1✔
661
    auto set_int_list = [](LstPtr<Int> list, const std::vector<int64_t>& value_list) {
8✔
662
        list->clear();
8✔
663
        for (auto i : value_list) {
20✔
664
            list->add(i);
20✔
665
        }
20✔
666
    };
8✔
667
    auto set_string_list = [](LstPtr<String> list, const std::vector<int64_t>& value_list) {
6✔
668
        list->clear();
6✔
669
        for (auto i : value_list) {
22✔
670
            if (i < 100) {
22✔
671
                std::string str("Str_");
20✔
672
                str += util::to_string(i);
20✔
673
                list->add(StringData(str));
20✔
674
            }
20✔
675
            else {
2✔
676
                list->add(StringData());
2✔
677
            }
2✔
678
        }
22✔
679
    };
6✔
680
    set_int_list(table->get_object(keys[0]).get_list_ptr<Int>(col_list_int), std::vector<Int>({0, 1}));
2✔
681
    set_int_list(table->get_object(keys[1]).get_list_ptr<Int>(col_list_int), std::vector<Int>({2, 3, 4, 5}));
2✔
682
    set_int_list(table->get_object(keys[2]).get_list_ptr<Int>(col_list_int), std::vector<Int>({6, 7, 8, 9}));
2✔
683
    set_int_list(table->get_object(keys[3]).get_list_ptr<Int>(col_list_int), std::vector<Int>({}));
2✔
684

1✔
685
    set_string_list(table->get_object(keys[0]).get_list_ptr<String>(col_list_string), std::vector<Int>({0, 1}));
2✔
686
    set_string_list(table->get_object(keys[1]).get_list_ptr<String>(col_list_string), std::vector<Int>({2, 3, 4, 5}));
2✔
687
    set_string_list(table->get_object(keys[2]).get_list_ptr<String>(col_list_string),
2✔
688
                    std::vector<Int>({6, 7, 100, 8, 9}));
2✔
689
    table->get_object(keys[0]).set(col_other, StringData("foo"));
2✔
690
    table->get_object(keys[1]).set(col_other, StringData("str"));
2✔
691
    table->get_object(keys[2]).set(col_other, StringData("str_9_baa"));
2✔
692

1✔
693
    Query q0 = table->column<Lst<Int>>(col_list_int) == 10;
2✔
694
    Query q1 = table->column<Lst<Int>>(col_list_int).max() > 5;
2✔
695
    Query q2 = table->column<Lst<String>>(col_list_string).begins_with("Str");
2✔
696
    Query q3 = table->column<Lst<String>>(col_list_string) == "Str_0";
2✔
697

1✔
698
    q0.find_all();
2✔
699
    q1.find_all();
2✔
700
    q2.find_all();
2✔
701
    q3.find_all();
2✔
702

1✔
703
    wt->commit();
2✔
704
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
705
    CHECK(metrics);
2✔
706
    std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
2✔
707
    CHECK(queries);
2✔
708

1✔
709
    CHECK_EQUAL(queries->size(), 4);
2✔
710

1✔
711
    std::string int_equal_description = queries->at(0).get_description();
2✔
712
    CHECK_EQUAL(int_equal_description, "10 == integers");
2✔
713

1✔
714
    std::string int_max_description = queries->at(1).get_description();
2✔
715
    CHECK_EQUAL(int_max_description, "5 < integers.@max");
2✔
716

1✔
717
    std::string str_begins_description = queries->at(2).get_description();
2✔
718
    CHECK_EQUAL(str_begins_description, "strings BEGINSWITH \"Str\"");
2✔
719

1✔
720
    std::string str_equal_description = queries->at(3).get_description();
2✔
721
    CHECK_EQUAL(str_equal_description, "\"Str_0\" == strings");
2✔
722
}
2✔
723

724
NONCONCURRENT_TEST(Metrics_TransactionTimings)
725
{
2✔
726
    ColKey col;
2✔
727
    SHARED_GROUP_TEST_PATH(path);
2✔
728
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
729
    DBOptions options(crypt_key());
2✔
730
    options.enable_metrics = true;
2✔
731
    DBRef sg = DB::create(*hist, path, options);
2✔
732
    CHECK(sg->get_metrics());
2✔
733
    {
2✔
734
        auto wt = sg->start_write();
2✔
735
        auto table = wt->add_table("table");
2✔
736
        col = table->add_column(type_Int, "first");
2✔
737
        std::vector<ObjKey> keys;
2✔
738
        table->create_objects(10, keys);
2✔
739
        wt->commit();
2✔
740
    }
2✔
741
    {
2✔
742
        auto rt = sg->start_read();
2✔
743
        auto table = rt->get_table("table");
2✔
744
        CHECK(bool(table));
2✔
745
        Query query = table->column<int64_t>(col) == 0;
2✔
746
        query.count();
2✔
747
        rt->end_read();
2✔
748
    }
2✔
749

1✔
750
    using namespace std::literals::chrono_literals;
2✔
751
    {
2✔
752
        ReadTransaction rt(sg);
2✔
753
        std::this_thread::sleep_for(60ms);
2✔
754
    }
2✔
755
    {
2✔
756
        WriteTransaction wt(sg);
2✔
757
        TableRef t = wt.get_table("table");
2✔
758
        t->create_object();
2✔
759
        std::this_thread::sleep_for(80ms);
2✔
760
        wt.commit();
2✔
761
    }
2✔
762
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
763
    CHECK(metrics);
2✔
764
    CHECK_NOT_EQUAL(metrics->num_query_metrics(), 0);
2✔
765
    CHECK_NOT_EQUAL(metrics->num_transaction_metrics(), 0);
2✔
766

1✔
767
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
768
    CHECK(transactions);
2✔
769
    CHECK_EQUAL(metrics->num_transaction_metrics(), 0);
2✔
770

1✔
771
    CHECK_EQUAL(transactions->size(), 4);
2✔
772

1✔
773
    for (auto t : *transactions) {
8✔
774
        CHECK_GREATER(t.get_transaction_time_nanoseconds(), 0);
8✔
775

4✔
776
        if (t.get_transaction_type() == TransactionInfo::read_transaction) {
8✔
777
            CHECK_EQUAL(t.get_fsync_time_nanoseconds(), 0.0);
4✔
778
            CHECK_EQUAL(t.get_write_time_nanoseconds(), 0.0);
4✔
779
        }
4✔
780
        else {
4✔
781
            if (!get_disable_sync_to_disk()) {
4✔
UNCOV
782
                CHECK_NOT_EQUAL(t.get_fsync_time_nanoseconds(), 0.0);
×
UNCOV
783
            }
×
784
            CHECK_NOT_EQUAL(t.get_write_time_nanoseconds(), 0.0);
4✔
785
            CHECK_LESS(t.get_fsync_time_nanoseconds(), t.get_transaction_time_nanoseconds());
4✔
786
            CHECK_LESS(t.get_write_time_nanoseconds(), t.get_transaction_time_nanoseconds());
4✔
787
        }
4✔
788
    }
8✔
789
    // test that the timings reported are in the right ballpark
1✔
790
    // read transaction
1✔
791
    CHECK_EQUAL(transactions->at(2).get_transaction_type(),
2✔
792
                metrics::TransactionInfo::TransactionType::read_transaction);
2✔
793
    CHECK_GREATER(transactions->at(2).get_transaction_time_nanoseconds(), 1'000);      // > 1us
2✔
794
    CHECK_LESS(transactions->at(2).get_transaction_time_nanoseconds(), 2'000'000'000); // < 2s
2✔
795
    CHECK_EQUAL(transactions->at(2).get_fsync_time_nanoseconds(), 0);                  // no fsync on read
2✔
796
    // write transaction
1✔
797
    CHECK_EQUAL(transactions->at(3).get_transaction_type(),
2✔
798
                metrics::TransactionInfo::TransactionType::write_transaction);
2✔
799
    CHECK_GREATER(transactions->at(3).get_transaction_time_nanoseconds(), 10'000);     // > 10us
2✔
800
    CHECK_LESS(transactions->at(3).get_transaction_time_nanoseconds(), 2'000'000'000); // <  2s
2✔
801
    // TODO: Investigate why this check is failing, since it sometimes fails and, since this is a
1✔
802
    // non-concurrent test, the sync_to_disk setting should not change during the execution of this test.
1✔
803
#if 0
804
    if (!get_disable_sync_to_disk()) {
805
        // This check returns 0 for get_fsync_time_nanoseconds if sync to disk is disabled
806
        CHECK_GREATER(transactions->at(3).get_fsync_time_nanoseconds(), 0); // fsync on write takes some time
807
    }
808
#endif
809
}
2✔
810

811

812
TEST(Metrics_TransactionData)
813
{
2✔
814
    SHARED_GROUP_TEST_PATH(path);
2✔
815
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
816
    DBOptions options(crypt_key());
2✔
817
    options.enable_metrics = true;
2✔
818
    DBRef sg = DB::create(*hist, path, options);
2✔
819
    populate(sg);
2✔
820

1✔
821
    {
2✔
822
        ReadTransaction rt(sg);
2✔
823
    }
2✔
824
    {
2✔
825
        auto wt = sg->start_write();
2✔
826
        auto table_keys = wt->get_table_keys();
2✔
827
        TableRef t0 = wt->get_table(table_keys[0]);
2✔
828
        TableRef t1 = wt->get_table(table_keys[1]);
2✔
829
        std::vector<ObjKey> keys;
2✔
830
        t0->create_objects(3, keys);
2✔
831
        t1->create_objects(7, keys);
2✔
832
        wt->commit();
2✔
833
    }
2✔
834

1✔
835
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
836
    CHECK(metrics);
2✔
837

1✔
838
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
839
    CHECK(transactions);
2✔
840
    CHECK_EQUAL(metrics->num_transaction_metrics(), 0);
2✔
841

1✔
842
    CHECK_EQUAL(transactions->size(), 3);
2✔
843

1✔
844
    CHECK_EQUAL(transactions->at(0).get_total_objects(), 11);
2✔
845
    CHECK_EQUAL(transactions->at(1).get_total_objects(), 11);
2✔
846
    CHECK_EQUAL(transactions->at(2).get_total_objects(), 11 + 3 + 7);
2✔
847
}
2✔
848

849
TEST(Metrics_TransactionVersions)
850
{
2✔
851
    SHARED_GROUP_TEST_PATH(path);
2✔
852
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
853
    DBOptions options(crypt_key());
2✔
854
    options.enable_metrics = true;
2✔
855
    DBRef sg = DB::create(*hist, path, options);
2✔
856
    populate(sg);
2✔
857
    const size_t num_writes_while_pinned = 10;
2✔
858
    TableKey tk0;
2✔
859
    TableKey tk1;
2✔
860
    {
2✔
861
        auto rt = sg->start_read();
2✔
862
        auto table_keys = rt->get_table_keys();
2✔
863
        tk0 = table_keys[0];
2✔
864
        tk1 = table_keys[1];
2✔
865
    }
2✔
866
    {
2✔
867
        auto wt = sg->start_write();
2✔
868
        TableRef t0 = wt->get_table(tk0);
2✔
869
        TableRef t1 = wt->get_table(tk1);
2✔
870
        std::vector<ObjKey> keys;
2✔
871
        t0->create_objects(3, keys);
2✔
872
        t1->create_objects(7, keys);
2✔
873
        wt->commit();
2✔
874
    }
2✔
875
    {
2✔
876
        std::unique_ptr<Replication> hist2(make_in_realm_history());
2✔
877
        DBRef sg2 = DB::create(*hist2, path, options);
2✔
878

1✔
879
        // Pin this version. Note that since this read transaction is against a different shared group
1✔
880
        // it doesn't get tracked in the transaction metrics of the original shared group.
1✔
881
        ReadTransaction rt(sg2);
2✔
882

1✔
883
        for (size_t i = 0; i < num_writes_while_pinned; ++i) {
22✔
884
            auto wt = sg->start_write();
20✔
885
            TableRef t0 = wt->get_table(tk0);
20✔
886
            t0->create_object();
20✔
887
            wt->commit();
20✔
888
        }
20✔
889
    }
2✔
890

1✔
891
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
892
    CHECK(metrics);
2✔
893

1✔
894
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
895
    CHECK(transactions);
2✔
896
    CHECK_EQUAL(metrics->num_transaction_metrics(), 0);
2✔
897

1✔
898
    CHECK_EQUAL(transactions->size(), 3 + num_writes_while_pinned);
2✔
899

1✔
900
    CHECK_EQUAL(transactions->at(0).get_num_available_versions(), 2);
2✔
901
    CHECK_EQUAL(transactions->at(1).get_num_available_versions(), 2);
2✔
902
    CHECK_EQUAL(transactions->at(2).get_num_available_versions(), 2);
2✔
903
    CHECK_EQUAL(transactions->at(3).get_num_available_versions(), 2);
2✔
904

1✔
905
    // from here intermediate versions are reclaimed, leaving us
1✔
906
    // with 3 active versions regardless of the number of commits
1✔
907
    for (size_t i = 1; i < num_writes_while_pinned; ++i) {
20✔
908
        CHECK_EQUAL(transactions->at(3 + i).get_num_available_versions(), 3);
18✔
909
    }
18✔
910
}
2✔
911

912
TEST(Metrics_MaxNumTransactionsIsNotExceeded)
913
{
2✔
914
    SHARED_GROUP_TEST_PATH(path);
2✔
915
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
916
    DBOptions options(crypt_key());
2✔
917
    options.enable_metrics = true;
2✔
918
    options.metrics_buffer_size = 10;
2✔
919
    auto sg = DB::create(*hist, path, options);
2✔
920
    populate(sg); // 1
2✔
921
    {
2✔
922
        ReadTransaction rt(sg); // 2
2✔
923
    }
2✔
924
    {
2✔
925
        WriteTransaction wt(sg); // 3
2✔
926
        TableRef t0 = wt.get_table("person");
2✔
927
        TableRef t1 = wt.get_table("pet");
2✔
928
        for (int i = 0; i < 3; i++) {
8✔
929
            t0->create_object();
6✔
930
        }
6✔
931
        for (int i = 0; i < 7; i++) {
16✔
932
            t1->create_object();
14✔
933
        }
14✔
934
        wt.commit();
2✔
935
    }
2✔
936

1✔
937
    for (size_t i = 0; i < options.metrics_buffer_size; ++i) {
22✔
938
        ReadTransaction rt(sg);
20✔
939
    }
20✔
940

1✔
941
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
942
    CHECK(metrics);
2✔
943

1✔
944
    CHECK_EQUAL(metrics->num_query_metrics(), 0);
2✔
945
    CHECK_EQUAL(metrics->num_transaction_metrics(), options.metrics_buffer_size);
2✔
946
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
947
    CHECK(transactions);
2✔
948
    for (auto transaction : *transactions) {
20✔
949
        CHECK_EQUAL(transaction.get_transaction_type(),
20✔
950
                    realm::metrics::TransactionInfo::TransactionType::read_transaction);
20✔
951
    }
20✔
952
}
2✔
953

954
TEST(Metrics_MaxNumQueriesIsNotExceeded)
955
{
2✔
956
    SHARED_GROUP_TEST_PATH(path);
2✔
957
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
958
    DBOptions options(crypt_key());
2✔
959
    options.enable_metrics = true;
2✔
960
    options.metrics_buffer_size = 10;
2✔
961
    auto sg = DB::create(*hist, path, options);
2✔
962

1✔
963
    {
2✔
964
        auto tr = sg->start_write();
2✔
965
        auto table = tr->add_table("table");
2✔
966
        table->add_column(type_Int, "col_int");
2✔
967
        for (int i = 0; i < 10; i++) {
22✔
968
            table->create_object();
20✔
969
        }
20✔
970
        tr->commit();
2✔
971
    }
2✔
972

1✔
973
    {
2✔
974
        auto rt = sg->start_read();
2✔
975
        auto table = rt->get_table("table");
2✔
976
        auto int_col = table->get_column_key("col_int");
2✔
977
        CHECK(bool(table));
2✔
978
        Query query = table->column<int64_t>(int_col) == 0;
2✔
979
        for (size_t i = 0; i < 2 * options.metrics_buffer_size; ++i) {
42✔
980
            query.find();
40✔
981
        }
40✔
982
    }
2✔
983
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
984
    CHECK(metrics);
2✔
985
    CHECK_EQUAL(metrics->num_query_metrics(), options.metrics_buffer_size);
2✔
986
}
2✔
987

988
// The number of decrypted pages is updated periodically by the governor.
989
// To test this, we need our own governor implementation which does not reclaim pages but runs at least once.
990
class NoPageReclaimGovernor : public realm::util::PageReclaimGovernor {
991
public:
992
    NoPageReclaimGovernor()
993
    {
4✔
994
        has_run_twice = will_run.get_future();
4✔
995
    }
4✔
996

997
    util::UniqueFunction<int64_t()> current_target_getter(size_t) override
998
    {
12✔
999
        return []() {
12✔
1000
            return realm::util::PageReclaimGovernor::no_match;
12✔
1001
        };
12✔
1002
    }
12✔
1003

1004
    void report_target_result(int64_t) override
1005
    {
12✔
1006
        if (run_count == 2) { // need to run twice before we're done
12✔
1007
            will_run.set_value();
4✔
1008
        }
4✔
1009
        ++run_count;
12✔
1010
    }
12✔
1011

1012
    std::future<void> has_run_twice;
1013
    std::promise<void> will_run;
1014
    int run_count = 0;
1015
};
1016

1017
// this test relies on the global state of the number of decrypted pages and therefore must be run in isolation
1018
NONCONCURRENT_TEST(Metrics_NumDecryptedPagesWithoutEncryption)
1019
{
2✔
1020
    SHARED_GROUP_TEST_PATH(path);
2✔
1021
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
1022
    DBOptions options(nullptr);
2✔
1023
    options.enable_metrics = true;
2✔
1024
    options.metrics_buffer_size = 10;
2✔
1025
    auto sg = DB::create(*hist, path, options);
2✔
1026

1✔
1027
    {
2✔
1028
        auto tr = sg->start_write();
2✔
1029
        tr->add_table("table");
2✔
1030

1✔
1031
#if REALM_ENABLE_ENCRYPTION
2✔
1032
        // we need this here because other unit tests might be using encryption and we need a guarantee
1✔
1033
        // that the global pages are from this shared group only.
1✔
1034
        NoPageReclaimGovernor gov;
2✔
1035
        realm::util::set_page_reclaim_governor(&gov);
2✔
1036
        // the remainder of the test suite should use the default.
1✔
1037
        auto on_exit = make_scope_exit([]() noexcept {
2✔
1038
            realm::util::set_page_reclaim_governor_to_default();
2✔
1039
        });
2✔
1040
        CHECK(gov.has_run_twice.valid());
2✔
1041
        REALM_ASSERT_RELEASE(gov.has_run_twice.wait_for(std::chrono::seconds(30)) == std::future_status::ready);
2✔
1042
#endif
2✔
1043

1✔
1044
        tr->commit();
2✔
1045
    }
2✔
1046

1✔
1047
    {
2✔
1048
        auto rt = sg->start_read();
2✔
1049
    }
2✔
1050

1✔
1051
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
1052
    CHECK(metrics);
2✔
1053

1✔
1054
    CHECK_EQUAL(metrics->num_transaction_metrics(), 2);
2✔
1055
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
1056
    CHECK(transactions);
2✔
1057
    CHECK_EQUAL(transactions->size(), 2);
2✔
1058
    CHECK_EQUAL(transactions->at(0).get_transaction_type(),
2✔
1059
                realm::metrics::TransactionInfo::TransactionType::write_transaction);
2✔
1060
    CHECK_EQUAL(transactions->at(0).get_num_decrypted_pages(), 0);
2✔
1061
    CHECK_EQUAL(transactions->at(1).get_transaction_type(),
2✔
1062
                realm::metrics::TransactionInfo::TransactionType::read_transaction);
2✔
1063
    CHECK_EQUAL(transactions->at(1).get_num_decrypted_pages(), 0);
2✔
1064
}
2✔
1065

1066
// this test relies on the global state of the number of decrypted pages and therefore must be run in isolation
1067
NONCONCURRENT_TEST_IF(Metrics_NumDecryptedPagesWithEncryption, REALM_ENABLE_ENCRYPTION)
1068
{
2✔
1069
    SHARED_GROUP_TEST_PATH(path);
2✔
1070
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
1071
    DBOptions options(crypt_key(true));
2✔
1072
    options.enable_metrics = true;
2✔
1073
    options.metrics_buffer_size = 10;
2✔
1074
    auto sg = DB::create(*hist, path, options);
2✔
1075

1✔
1076
    {
2✔
1077
        auto tr = sg->start_write();
2✔
1078
        tr->add_table("table");
2✔
1079

1✔
1080
#if REALM_ENABLE_ENCRYPTION
2✔
1081
        NoPageReclaimGovernor gov;
2✔
1082
        realm::util::set_page_reclaim_governor(&gov);
2✔
1083
        // the remainder of the test suite should use the default.
1✔
1084
        auto on_exit = make_scope_exit([]() noexcept {
2✔
1085
            realm::util::set_page_reclaim_governor_to_default();
2✔
1086
        });
2✔
1087
        CHECK(gov.has_run_twice.valid());
2✔
1088
        REALM_ASSERT_RELEASE(gov.has_run_twice.wait_for(std::chrono::seconds(30)) == std::future_status::ready);
2✔
1089
#endif
2✔
1090

1✔
1091
        tr->commit();
2✔
1092
    }
2✔
1093

1✔
1094
    {
2✔
1095
        auto rt = sg->start_read();
2✔
1096
    }
2✔
1097

1✔
1098
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
1099
    CHECK(metrics);
2✔
1100

1✔
1101
    CHECK_EQUAL(metrics->num_transaction_metrics(), 2);
2✔
1102
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
1103
    CHECK(transactions);
2✔
1104
    CHECK_EQUAL(transactions->size(), 2);
2✔
1105
    CHECK_EQUAL(transactions->at(0).get_transaction_type(),
2✔
1106
                realm::metrics::TransactionInfo::TransactionType::write_transaction);
2✔
1107
    CHECK_EQUAL(transactions->at(0).get_num_decrypted_pages(), 1);
2✔
1108
    CHECK_EQUAL(transactions->at(1).get_transaction_type(),
2✔
1109
                realm::metrics::TransactionInfo::TransactionType::read_transaction);
2✔
1110
    CHECK_EQUAL(transactions->at(1).get_num_decrypted_pages(), 1);
2✔
1111
}
2✔
1112

1113
TEST(Metrics_MemoryChecks)
1114
{
2✔
1115
    SHARED_GROUP_TEST_PATH(path);
2✔
1116
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
1117
    DBOptions options(nullptr);
2✔
1118
    options.enable_metrics = true;
2✔
1119
    options.metrics_buffer_size = 10;
2✔
1120
    auto sg = DB::create(*hist, path, options);
2✔
1121
    populate(sg);
2✔
1122

1✔
1123
    {
2✔
1124
        auto rt = sg->start_read();
2✔
1125
    }
2✔
1126

1✔
1127
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
2✔
1128
    CHECK(metrics);
2✔
1129

1✔
1130
    CHECK_EQUAL(metrics->num_transaction_metrics(), 2);
2✔
1131
    std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
2✔
1132
    CHECK(transactions);
2✔
1133

1✔
1134
    for (auto transaction : *transactions) {
4✔
1135
        CHECK_GREATER(transaction.get_disk_size(), 0);
4✔
1136
        CHECK_GREATER(transaction.get_free_space(), 0);
4✔
1137
    }
4✔
1138
}
2✔
1139

1140
#else // REALM_METRICS
1141

1142
TEST(Metrics_APIAvailability)
1143
{
1144
    SHARED_GROUP_TEST_PATH(path);
1145
    std::unique_ptr<Replication> hist(make_in_realm_history());
1146
    DBOptions options(crypt_key());
1147
    options.enable_metrics = true;
1148
    DBRef sg = DB::create(*hist, path, options);
1149
    CHECK(!sg->get_metrics());
1150
    {
1151
        auto tr = sg->start_write();
1152
        auto table = tr->add_table("table");
1153
        table->add_column(type_Int, "first");
1154
        for (int i = 0; i < 10; i++) {
1155
            table->create_object();
1156
        }
1157
        tr->commit();
1158
    }
1159

1160
    {
1161
        ReadTransaction rt(sg);
1162
        auto table = rt.get_table("table");
1163
        auto col = table->get_column_key("first");
1164
        CHECK(bool(table));
1165
        Query q = table->column<int64_t>(col) == 0;
1166
        q.count();
1167
    }
1168
    std::shared_ptr<Metrics> metrics = sg->get_metrics();
1169

1170
    // the following will never execute since when REALM_METRICS is undefined,
1171
    // then sg.get_metrics() will always return a nullptr, however, the purpose
1172
    // of the remainder of the test is to ensure that all of the methods below
1173
    // are still accessible at compile time so that users of core do not need to check
1174
    // REALM_METRICS, but can use a core with or without the flag in the same way.
1175
    if (metrics) {
1176
        CHECK_EQUAL(metrics->num_transaction_metrics(), 0);
1177
        CHECK_EQUAL(metrics->num_query_metrics(), 0);
1178
        std::unique_ptr<Metrics::TransactionInfoList> transactions = metrics->take_transactions();
1179

1180
        if (transactions) {
1181
            for (auto transaction : *transactions) {
1182
                transaction.get_disk_size();
1183
                transaction.get_free_space();
1184
                transaction.get_transaction_time();
1185
                transaction.get_fsync_time();
1186
                transaction.get_write_time();
1187
                transaction.get_disk_size();
1188
                transaction.get_free_space();
1189
                transaction.get_total_objects();
1190
                transaction.get_num_available_versions();
1191
                transaction.get_num_decrypted_pages();
1192
            }
1193
        }
1194
        std::unique_ptr<Metrics::QueryInfoList> queries = metrics->take_queries();
1195
        if (queries) {
1196
            for (auto query : *queries) {
1197
                query.get_description();
1198
                query.get_table_name();
1199
                query.get_type();
1200
                query.get_query_time();
1201
            }
1202
        }
1203
    }
1204
}
1205

1206
#endif // REALM_METRICS
1207
#endif // TEST_METRICS
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc