Skip to content

Commit 00935d2

Browse files
Disable mmap usage for leveldb storage (#1187)
The usage of mmap causes crashes on memory constrained environment. By overriding the default leveldb environment we can avoid it's usage. Resolves: OLPSUP-13838 Signed-off-by: Mykhailo Kuchma <[email protected]>
1 parent d99bc9d commit 00935d2

File tree

4 files changed

+250
-4
lines changed

4 files changed

+250
-4
lines changed

olp-cpp-sdk-core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ set(OLP_SDK_CACHE_SOURCES
222222
./src/cache/DefaultCacheImpl.h
223223
./src/cache/DiskCache.cpp
224224
./src/cache/DiskCache.h
225+
./src/cache/DiskCacheEnv.cpp
226+
./src/cache/DiskCacheEnv.h
225227
./src/cache/DiskCacheSizeLimitEnv.cpp
226228
./src/cache/DiskCacheSizeLimitEnv.h
227229
./src/cache/DiskCacheSizeLimitWritableFile.cpp

olp-cpp-sdk-core/src/cache/DiskCache.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <leveldb/iterator.h>
3131
#include <leveldb/options.h>
3232
#include <leveldb/write_batch.h>
33+
#include "DiskCacheEnv.h"
3334
#include "DiskCacheSizeLimitEnv.h"
3435
#include "ReadOnlyEnv.h"
3536
#include "olp/core/logging/Log.h"
@@ -51,7 +52,7 @@ static bool RepairCache(const std::string& data_path) {
5152
auto status = leveldb::RepairDB(data_path, leveldb::Options());
5253
if (status.ok()) {
5354
OLP_SDK_LOG_INFO(kLogTag, "RepairCache: repaired - " << data_path);
54-
leveldb::Env::Default()->DeleteDir(data_path + "/lost");
55+
DiskCacheEnv::Env()->DeleteDir(data_path + "/lost");
5556
return true;
5657
}
5758
OLP_SDK_LOG_ERROR(kLogTag,
@@ -73,7 +74,7 @@ static bool RepairCache(const std::string& data_path) {
7374
void RemoveOtherDB(const std::string& data_path,
7475
const std::string& data_path_to_keep) {
7576
std::vector<std::string> path_contents;
76-
auto status = leveldb::Env::Default()->GetChildren(data_path, &path_contents);
77+
auto status = DiskCacheEnv::Env()->GetChildren(data_path, &path_contents);
7778
if (!status.ok()) {
7879
OLP_SDK_LOG_WARNING(kLogTag, "RemoveOtherDB: failed to list folder \""
7980
<< data_path << "\" contents - "
@@ -183,12 +184,12 @@ OpenResult DiskCache::Open(const std::string& data_path,
183184

184185
if (max_size_ != kSizeMax) {
185186
environment_ = std::make_unique<DiskCacheSizeLimitEnv>(
186-
leveldb::Env::Default(), versioned_data_path,
187+
DiskCacheEnv::Env(), versioned_data_path,
187188
settings.enforce_immediate_flush);
188189
open_options.env = environment_.get();
189190
}
190191
} else {
191-
environment_ = std::make_unique<ReadOnlyEnv>(leveldb::Env::Default());
192+
environment_ = std::make_unique<ReadOnlyEnv>(DiskCacheEnv::Env());
192193
open_options.env = environment_.get();
193194
}
194195

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
* Copyright (C) 2021 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
#include "DiskCacheEnv.h"
21+
22+
#include <olp/core/porting/platform.h>
23+
24+
#ifndef PORTING_PLATFORM_WINDOWS
25+
26+
#include <fcntl.h>
27+
#include <sys/resource.h>
28+
#include <sys/stat.h>
29+
#include <sys/time.h>
30+
#include <unistd.h>
31+
32+
#include <atomic>
33+
#include <cstring>
34+
#include <thread>
35+
36+
namespace {
37+
38+
leveldb::Status PosixError(const std::string& context, int error_number) {
39+
if (error_number == ENOENT) {
40+
return leveldb::Status::NotFound(context, std::strerror(error_number));
41+
} else {
42+
return leveldb::Status::IOError(context, std::strerror(error_number));
43+
}
44+
}
45+
46+
// Return the maximum number of read-only files to keep open.
47+
int MaxOpenFiles() {
48+
static int open_read_only_file_limit = -1;
49+
if (open_read_only_file_limit >= 0) {
50+
return open_read_only_file_limit;
51+
}
52+
struct ::rlimit rlim;
53+
if (::getrlimit(RLIMIT_NOFILE, &rlim)) {
54+
// getrlimit failed, fallback to hard-coded default.
55+
open_read_only_file_limit = 50;
56+
} else if (rlim.rlim_cur == RLIM_INFINITY) {
57+
open_read_only_file_limit = std::numeric_limits<int>::max();
58+
} else {
59+
// Allow use of 20% of available file descriptors for read-only files.
60+
open_read_only_file_limit = static_cast<int>(rlim.rlim_cur) / 5;
61+
}
62+
return open_read_only_file_limit;
63+
}
64+
65+
class Limiter {
66+
public:
67+
// Limit maximum number of resources to |max_acquires|.
68+
Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
69+
70+
Limiter(const Limiter&) = delete;
71+
Limiter operator=(const Limiter&) = delete;
72+
73+
// If another resource is available, acquire it and return true.
74+
// Else return false.
75+
bool Acquire() {
76+
int old_acquires_allowed =
77+
acquires_allowed_.fetch_sub(1, std::memory_order_relaxed);
78+
79+
if (old_acquires_allowed > 0)
80+
return true;
81+
82+
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
83+
return false;
84+
}
85+
86+
// Release a resource acquired by a previous call to Acquire() that returned
87+
// true.
88+
void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
89+
90+
private:
91+
// The number of available resources.
92+
//
93+
// This is a counter and is not tied to the invariants of any other class, so
94+
// it can be operated on safely using std::memory_order_relaxed.
95+
std::atomic<int> acquires_allowed_;
96+
};
97+
98+
// Implements random read access in a file using pread().
99+
//
100+
// Instances of this class are thread-safe, as required by the RandomAccessFile
101+
// API. Instances are immutable and Read() only calls thread-safe library
102+
// functions.
103+
class PosixRandomAccessFile final : public leveldb::RandomAccessFile {
104+
public:
105+
// The new instance takes ownership of |fd|. |fd_limiter| must outlive this
106+
// instance, and will be used to determine if .
107+
PosixRandomAccessFile(std::string filename, int fd, Limiter* fd_limiter)
108+
: has_permanent_fd_(fd_limiter->Acquire()),
109+
fd_(has_permanent_fd_ ? fd : -1),
110+
filename_(std::move(filename)),
111+
fd_limiter_(fd_limiter) {
112+
if (!has_permanent_fd_) {
113+
assert(fd_ == -1);
114+
::close(fd); // The file will be opened on every read.
115+
}
116+
}
117+
118+
~PosixRandomAccessFile() override {
119+
if (has_permanent_fd_) {
120+
assert(fd_ != -1);
121+
::close(fd_);
122+
fd_limiter_->Release();
123+
}
124+
}
125+
126+
leveldb::Status Read(uint64_t offset, size_t n, leveldb::Slice* result,
127+
char* scratch) const override {
128+
int fd = fd_;
129+
130+
if (!has_permanent_fd_) {
131+
fd = ::open(filename_.c_str(), O_RDONLY);
132+
if (fd < 0) {
133+
return PosixError(filename_, errno);
134+
}
135+
}
136+
137+
assert(fd != -1);
138+
139+
leveldb::Status status;
140+
ssize_t read_size = ::pread(fd, scratch, n, static_cast<off_t>(offset));
141+
*result = leveldb::Slice(scratch, (read_size < 0) ? 0 : read_size);
142+
if (read_size < 0) {
143+
// An error: return a non-ok status.
144+
status = PosixError(filename_, errno);
145+
}
146+
147+
if (!has_permanent_fd_) {
148+
// Close the temporary file descriptor opened earlier.
149+
assert(fd != fd_);
150+
::close(fd);
151+
}
152+
153+
return status;
154+
}
155+
156+
private:
157+
/// If false, the file is opened on every read.
158+
const bool has_permanent_fd_;
159+
/// File descriptor, -1 if has_permanent_fd_ is false.
160+
const int fd_;
161+
/// File name
162+
const std::string filename_;
163+
164+
Limiter* const fd_limiter_;
165+
};
166+
167+
class EnvWrapper : public leveldb::EnvWrapper {
168+
public:
169+
EnvWrapper()
170+
: leveldb::EnvWrapper(leveldb::Env::Default()),
171+
fd_limiter_(MaxOpenFiles()) {}
172+
173+
~EnvWrapper() = default;
174+
175+
leveldb::Status NewRandomAccessFile(
176+
const std::string& filename,
177+
leveldb::RandomAccessFile** result) override {
178+
*result = nullptr;
179+
int fd = ::open(filename.c_str(), O_RDONLY);
180+
if (fd < 0) {
181+
return PosixError(filename, errno);
182+
}
183+
184+
*result = new PosixRandomAccessFile(filename, fd, &fd_limiter_);
185+
return leveldb::Status::OK();
186+
}
187+
188+
private:
189+
Limiter fd_limiter_;
190+
};
191+
192+
} // namespace
193+
194+
#endif // PORTING_PLATFORM_WINDOWS
195+
196+
namespace olp {
197+
namespace cache {
198+
199+
leveldb::Env* DiskCacheEnv::Env() {
200+
#ifdef PORTING_PLATFORM_WINDOWS
201+
return leveldb::Env::Default();
202+
#else
203+
static EnvWrapper env;
204+
return &env;
205+
#endif // PORTING_PLATFORM_WINDOWS
206+
}
207+
208+
} // namespace cache
209+
} // namespace olp
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (C) 2021 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
#pragma once
21+
22+
#include <leveldb/env.h>
23+
24+
namespace olp {
25+
namespace cache {
26+
27+
/// The wrapper for a leveldb default environment
28+
class DiskCacheEnv {
29+
public:
30+
static leveldb::Env* Env();
31+
};
32+
33+
} // namespace cache
34+
} // namespace olp

0 commit comments

Comments
 (0)