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

mendersoftware / mender / 2271300743

19 Jan 2026 11:42AM UTC coverage: 81.376% (+1.7%) from 79.701%
2271300743

push

gitlab-ci

web-flow
Merge pull request #1879 from lluiscampos/MEN-8687-ci-debian-updates

MEN-8687: Update Debian base images for CI jobs

8791 of 10803 relevant lines covered (81.38%)

20310.08 hits per line

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

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

15
#include <common/key_value_database_lmdb.hpp>
16

17
#include <filesystem>
18

19
#include <lmdbxx/lmdb++.h>
20

21
#include <common/common.hpp>
22
#include <common/log.hpp>
23

24
namespace mender {
25
namespace common {
26
namespace key_value_database {
27

28
namespace fs = std::filesystem;
29

30
namespace log = mender::common::log;
31

32
const string kBrokenSuffix {"-broken"};
33

34
class LmdbTransaction : public Transaction {
3,250✔
35
public:
36
        LmdbTransaction(lmdb::txn &txn, lmdb::dbi &dbi);
37

38
        expected::ExpectedBytes Read(const string &key) override;
39
        error::Error Write(const string &key, const vector<uint8_t> &value) override;
40
        error::Error Remove(const string &key) override;
41

42
private:
43
        lmdb::txn &txn_;
44
        lmdb::dbi &dbi_;
45
};
46

47
LmdbTransaction::LmdbTransaction(lmdb::txn &txn, lmdb::dbi &dbi) :
3,250✔
48
        txn_ {txn},
3,250✔
49
        dbi_ {dbi} {
3,250✔
50
}
×
51

52
expected::ExpectedBytes LmdbTransaction::Read(const string &key) {
1,977✔
53
        try {
54
                std::string_view value;
1,977✔
55
                bool exists = dbi_.get(txn_, key, value);
1,977✔
56
                if (!exists) {
1,977✔
57
                        return expected::unexpected(
1,166✔
58
                                MakeError(KeyError, "Key " + key + " not found in database"));
3,498✔
59
                }
60

61
                return common::ByteVectorFromString(value);
1,622✔
62
        } catch (std::runtime_error &e) {
×
63
                return expected::unexpected(MakeError(LmdbError, e.what()));
×
64
        }
×
65
}
66

67
error::Error LmdbTransaction::Write(const string &key, const vector<uint8_t> &value) {
1,486✔
68
        try {
69
                string_view data(reinterpret_cast<const char *>(value.data()), value.size());
70
                bool exists = dbi_.put(txn_, key, data);
1,486✔
71
                if (!exists) {
1,486✔
72
                        return MakeError(AlreadyExistsError, "Key " + key + " already exists");
×
73
                }
74

75
                return error::NoError;
1,486✔
76
        } catch (std::runtime_error &e) {
×
77
                return MakeError(LmdbError, e.what());
×
78
        }
×
79
}
80

81
error::Error LmdbTransaction::Remove(const string &key) {
1,923✔
82
        try {
83
                // We don't treat !exists as an error, just ignore return code.
84
                dbi_.del(txn_, key);
1,923✔
85

86
                return error::NoError;
1,923✔
87
        } catch (std::runtime_error &e) {
×
88
                return MakeError(LmdbError, e.what());
×
89
        }
×
90
}
91

92
KeyValueDatabaseLmdb::KeyValueDatabaseLmdb() {
1,074✔
93
}
1,074✔
94

95
KeyValueDatabaseLmdb::~KeyValueDatabaseLmdb() {
892✔
96
        Close();
446✔
97
}
1,338✔
98

99
error::Error KeyValueDatabaseLmdb::Open(const string &path) {
491✔
100
        return OpenInternal(path, true);
491✔
101
}
102

103
error::Error KeyValueDatabaseLmdb::OpenInternal(const string &path, bool try_recovery) {
492✔
104
        Close();
492✔
105

106
        env_ = make_unique<lmdb::env>(lmdb::env::create());
1,476✔
107

108
        try {
109
                env_->open(path.c_str(), MDB_NOSUBDIR, 0600);
492✔
110
        } catch (std::runtime_error &e) {
4✔
111
                auto err {MakeError(LmdbError, e.what()).WithContext("Opening LMDB database failed")};
8✔
112

113
                if (not try_recovery) {
4✔
114
                        env_.reset();
115
                        return err;
×
116
                }
117

118
                try {
119
                        if (not fs::exists(path)) {
4✔
120
                                env_.reset();
121
                                return err;
2✔
122
                        }
123

124
                        log::Warning(
2✔
125
                                "Failure to open database. Attempting to recover by resetting. The original database can be found at `"
126
                                + path + kBrokenSuffix + "`");
2✔
127

128
                        fs::rename(path, path + kBrokenSuffix);
3✔
129

130
                        auto err2 = OpenInternal(path, false);
1✔
131
                        if (err2 != error::NoError) {
1✔
132
                                return err.FollowedBy(err2);
×
133
                        }
134
                        return error::NoError;
1✔
135
                } catch (fs::filesystem_error &e) {
1✔
136
                        env_.reset();
137
                        return err.FollowedBy(error::Error(e.code().default_error_condition(), e.what())
2✔
138
                                                                          .WithContext("Opening LMDB database failed"));
2✔
139
                } catch (std::runtime_error &e) {
1✔
140
                        env_.reset();
141
                        return err.FollowedBy(error::MakeError(error::GenericError, e.what())
×
142
                                                                          .WithContext("Opening LMDB database failed"));
×
143
                }
×
144
        }
4✔
145

146
        return error::NoError;
488✔
147
}
148

149
void KeyValueDatabaseLmdb::Close() {
941✔
150
        env_.reset();
151
}
941✔
152

153
expected::ExpectedBytes KeyValueDatabaseLmdb::Read(const string &key) {
264✔
154
        vector<uint8_t> ret;
155
        auto err = ReadTransaction([&key, &ret](Transaction &txn) -> error::Error {
264✔
156
                auto result = txn.Read(key);
264✔
157
                if (result) {
264✔
158
                        ret = std::move(result.value());
111✔
159
                        return error::NoError;
111✔
160
                } else {
161
                        return result.error();
153✔
162
                }
163
        });
264✔
164
        if (mender::common::error::NoError != err) {
264✔
165
                return expected::unexpected(err);
306✔
166
        } else {
167
                return ret;
111✔
168
        }
169
}
170

171
error::Error KeyValueDatabaseLmdb::Write(const string &key, const vector<uint8_t> &value) {
279✔
172
        return WriteTransaction(
279✔
173
                [&key, &value](Transaction &txn) -> error::Error { return txn.Write(key, value); });
837✔
174
}
175

176
error::Error KeyValueDatabaseLmdb::Remove(const string &key) {
961✔
177
        return WriteTransaction([&key](Transaction &txn) -> error::Error { return txn.Remove(key); });
2,883✔
178
}
179

180
error::Error KeyValueDatabaseLmdb::WriteTransaction(function<error::Error(Transaction &)> txnFunc) {
2,570✔
181
        AssertOrReturnError(env_);
2,570✔
182

183
        try {
184
                lmdb::txn lmdb_txn = lmdb::txn::begin(*env_, nullptr, 0);
2,570✔
185
                lmdb::dbi lmdb_dbi = lmdb::dbi::open(lmdb_txn, nullptr, 0);
2,570✔
186
                LmdbTransaction txn(lmdb_txn, lmdb_dbi);
187
                auto error = txnFunc(txn);
2,570✔
188
                if (error::NoError != error) {
2,570✔
189
                        lmdb_txn.abort();
58✔
190
                } else {
191
                        lmdb_txn.commit();
2,512✔
192
                }
193
                return error;
2,570✔
194
        } catch (std::runtime_error &e) {
2,570✔
195
                return MakeError(LmdbError, e.what());
×
196
        }
×
197
}
198

199
error::Error KeyValueDatabaseLmdb::ReadTransaction(function<error::Error(Transaction &)> txnFunc) {
681✔
200
        AssertOrReturnError(env_);
682✔
201

202
        try {
203
                lmdb::txn lmdb_txn = lmdb::txn::begin(*env_, nullptr, MDB_RDONLY);
680✔
204
                lmdb::dbi lmdb_dbi = lmdb::dbi::open(lmdb_txn, nullptr, 0);
680✔
205
                LmdbTransaction txn(lmdb_txn, lmdb_dbi);
206
                return txnFunc(txn);
680✔
207
        } catch (std::runtime_error &e) {
680✔
208
                return MakeError(LmdbError, e.what());
×
209
        }
×
210
}
211

212
} // namespace key_value_database
213
} // namespace common
214
} // namespace mender
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