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

realm / realm-core / nicola.cabiddu_1042

27 Sep 2023 06:04PM UTC coverage: 91.085% (-1.8%) from 92.915%
nicola.cabiddu_1042

Pull #6766

Evergreen

nicola-cab
Fix logic for dictionaries
Pull Request #6766: Client Reset for collections in mixed / nested collections

97276 of 178892 branches covered (0.0%)

1994 of 2029 new or added lines in 7 files covered. (98.28%)

4556 existing lines in 112 files now uncovered.

237059 of 260260 relevant lines covered (91.09%)

6321099.55 hits per line

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

87.02
/src/realm/backup_restore.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 <realm/backup_restore.hpp>
20
#include <realm/util/file.hpp>
21
#include <realm/util/time.hpp>
22

23
#include <vector>
24
#include <chrono>
25

26
namespace realm {
27

28
/*
29
 * IMPORTANT: The following two arrays must be kept updated
30
 * as new versions are released or if rollback is ever done.
31
 */
32

33
using VersionList = BackupHandler::VersionList;
34
using VersionTimeList = BackupHandler::VersionTimeList;
35

36
// Note: accepted versions should have new versions added at front
37
const VersionList BackupHandler::accepted_versions_ = {24, 23, 22, 21, 20, 11, 10};
38

39
// the pair is <version, age-in-seconds>
40
// we keep backup files in 3 months.
41
static constexpr int three_months = 3 * 31 * 24 * 60 * 60;
42
const VersionTimeList BackupHandler::delete_versions_{{23, three_months}, {22, three_months}, {21, three_months},
43
                                                      {20, three_months}, {11, three_months}, {10, three_months}};
44

45

46
// helper functions
47
static std::string backup_name(const std::string& prefix, int version)
48
{
840,021✔
49
    return util::format("%1v%2.backup.realm", prefix, version);
840,021✔
50
}
840,021✔
51

52
static bool backup_exists(const std::string& prefix, int version)
53
{
838,863✔
54
    std::string fname = backup_name(prefix, version);
838,863✔
55
    return util::File::exists(fname);
838,863✔
56
}
838,863✔
57

58
std::string BackupHandler::get_prefix_from_path(const std::string& path)
59
{
139,866✔
60
    auto size = path.size();
139,866✔
61

68,766✔
62
    // remove a suffix ".realm" but add back the "."
68,766✔
63
    if (size > 6 && path.substr(size - 6, 6) == ".realm") {
139,866✔
64
        return path.substr(0, size - 5); // include '.'
111,261✔
65
    }
111,261✔
66

14,172✔
67
    // if no ".realm" suffix, at least ensure a terminating "."
14,172✔
68
    if (path[size - 1] == '.') {
28,605✔
UNCOV
69
        return path;
×
70
    }
×
71
    return path + ".";
28,605✔
72
}
28,605✔
73

74
BackupHandler::BackupHandler(const std::string& path, const VersionList& accepted,
75
                             const VersionTimeList& to_be_deleted)
76
{
139,854✔
77
    m_path = path;
139,854✔
78
    m_prefix = get_prefix_from_path(path);
139,854✔
79
    m_accepted_versions = accepted;
139,854✔
80
    m_delete_versions = to_be_deleted;
139,854✔
81
}
139,854✔
82

83
bool BackupHandler::must_restore_from_backup(int current_file_format_version) const
84
{
139,854✔
85
    if (current_file_format_version == 0)
139,854✔
86
        return false;
51,249✔
87
    if (is_accepted_file_format(current_file_format_version))
88,605✔
88
        return false;
88,581✔
89
    for (auto v : m_accepted_versions) {
102✔
90
        if (backup_exists(m_prefix, v))
102✔
91
            return true;
12✔
92
    }
102✔
93
    return false;
18✔
94
}
24✔
95

96
bool BackupHandler::is_accepted_file_format(int version) const noexcept
97
{
177,198✔
98
    for (auto v : m_accepted_versions) {
178,692✔
99
        if (v == version)
178,692✔
100
            return true;
177,162✔
101
    }
178,692✔
102
    return false;
87,606✔
103
}
177,198✔
104

105
void BackupHandler::restore_from_backup()
106
{
12✔
107
    for (auto v : m_accepted_versions) {
18✔
108
        if (backup_exists(m_prefix, v)) {
18✔
109
            prep_logging();
12✔
110
            auto backup_nm = backup_name(m_prefix, v);
12✔
111
            m_logger->info("%1 : Restoring from backup: %2", m_time_buf, backup_nm);
12✔
112
            util::File::move(backup_nm, m_path);
12✔
113
            return;
12✔
114
        }
12✔
115
    }
18✔
116
}
12✔
117

118
void BackupHandler::cleanup_backups()
119
{
139,842✔
120
    auto now = time(nullptr);
139,842✔
121
    for (auto v : m_delete_versions) {
838,746✔
122
        try {
838,746✔
123
            if (backup_exists(m_prefix, v.first)) {
838,746✔
124
                std::string fn = backup_name(m_prefix, v.first);
894✔
125
                // Assuming time_t is in seconds (should be on posix, but...)
423✔
126
                auto last_modified = util::File::last_write_time(fn);
894✔
127
                double diff = difftime(now, last_modified);
894✔
128
                if (diff > v.second) {
894✔
129
                    prep_logging();
6✔
130
                    m_logger->info("%1 : Removing old backup: %2   (age %3)", m_time_buf, fn, diff);
6✔
131
                    util::File::remove(fn);
6✔
132
                }
6✔
133
            }
894✔
134
        }
838,746✔
135
        catch (...) // ignore any problems, just leave the files
838,746✔
136
        {
412,371✔
UNCOV
137
        }
×
138
    }
838,746✔
139
}
139,842✔
140

141
void BackupHandler::ensure_logger()
142
{
306✔
143
    m_logger = std::make_unique<util::AppendToFileLogger>(m_path + ".backup-log");
306✔
144
}
306✔
145

146
void BackupHandler::prep_logging()
147
{
306✔
148
    ensure_logger();
306✔
149
    // preformat time string for later logging
153✔
150
    std::time_t t = std::time(nullptr);
306✔
151
    auto tm = util::gmtime(t);
306✔
152
    std::strftime(m_time_buf, sizeof(m_time_buf), "%c", &tm);
306✔
153
}
306✔
154

155

156
void BackupHandler::backup_realm_if_needed(int current_file_format_version, int target_file_format_version)
157
{
139,833✔
158
    if (current_file_format_version == 0)
139,833✔
159
        return;
51,249✔
160
    if (current_file_format_version >= target_file_format_version)
88,584✔
161
        return;
88,332✔
162
    std::string backup_nm = backup_name(m_prefix, current_file_format_version);
252✔
163
    if (util::File::exists(backup_nm)) {
252✔
164
        return;
108✔
165
    }
108✔
166
    try {
144✔
167
        // ignore it, if attempt to get free space fails for any reason
72✔
168
        if (util::File::get_free_space(m_path) < util::File::get_size_static(m_path) * 2) {
144✔
UNCOV
169
            prep_logging();
×
170
            m_logger->error("%1 : Insufficient free space for backup: %2", m_time_buf, backup_nm);
×
171
            return;
×
172
        }
×
173
    }
×
174
    catch (...) {
×
175
        // ignore error
176
    }
×
177
    {
144✔
178
        prep_logging();
144✔
179
        m_logger->info("%1 : Creating backup: %2", m_time_buf, backup_nm);
144✔
180
    }
144✔
181
    std::string part_name = backup_nm + ".part";
144✔
182
    // The backup file should be a 1-1 copy, so that we can get the original
72✔
183
    // contents including unchanged layout of data, freelists, etc
72✔
184
    // In doing so we forego the option of compacting the backup.
72✔
185
    // Silence any errors during the backup process, but should one occur
72✔
186
    // remove any backup files, since they cannot be trusted.
72✔
187
    try {
144✔
188
        util::File::copy(m_path, part_name);
144✔
189
        util::File::move(part_name, backup_nm);
144✔
190
        prep_logging();
144✔
191
        m_logger->info("%1 : Completed backup: %2", m_time_buf, backup_nm);
144✔
192
    }
144✔
193
    catch (...) {
72✔
UNCOV
194
        try {
×
195
            util::File::try_remove(part_name);
×
196
            util::File::try_remove(backup_nm);
×
197
        }
×
198
        catch (...) {
×
199
        }
×
200
    }
×
201
}
144✔
202

203
} // namespace realm
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