|
8 | 8 | #include "eden/fs/inodes/sqlitecatalog/SqliteTreeStore.h"
|
9 | 9 |
|
10 | 10 | #include <folly/Range.h>
|
| 11 | +#include <folly/stop_watch.h> |
11 | 12 | #include <array>
|
12 | 13 | #include "eden/fs/inodes/InodeNumber.h"
|
13 | 14 | #include "eden/fs/inodes/overlay/gen-cpp2/overlay_types.h"
|
14 | 15 | #include "eden/fs/sqlite/PersistentSqliteStatement.h"
|
15 | 16 | #include "eden/fs/sqlite/SqliteStatement.h"
|
| 17 | +#include "eden/fs/telemetry/StructuredLogger.h" |
16 | 18 | #include "eden/fs/utils/DirType.h"
|
17 | 19 |
|
18 | 20 | namespace facebook::eden {
|
@@ -119,12 +121,73 @@ struct SqliteTreeStore::StatementCache {
|
119 | 121 | std::array<PersistentSqliteStatement, kBatchInsertSize> batchInsert;
|
120 | 122 | };
|
121 | 123 |
|
| 124 | +namespace { |
| 125 | +std::unique_ptr<SqliteDatabase> removeAndRecreateDb(AbsolutePathPiece path) { |
| 126 | + int rc = ::unlink(path.copy().c_str()); |
| 127 | + if (rc != 0 && errno != ENOENT) { |
| 128 | + throw_<std::runtime_error>( |
| 129 | + "unable to remove sqlite database ", path, ", errno: ", errno); |
| 130 | + } |
| 131 | + return std::make_unique<SqliteDatabase>(path); |
| 132 | +} |
| 133 | + |
| 134 | +std::unique_ptr<SqliteDatabase> openAndVerifyDb( |
| 135 | + AbsolutePathPiece path, |
| 136 | + std::shared_ptr<StructuredLogger> logger) { |
| 137 | + try { |
| 138 | + auto db = std::make_unique<SqliteDatabase>(path); |
| 139 | + |
| 140 | + std::vector<std::string> errors; |
| 141 | + errors.reserve(0); |
| 142 | + |
| 143 | + folly::stop_watch<> integrityCheckRuntime; |
| 144 | + { |
| 145 | + auto dbLock = db->lock(); |
| 146 | + auto stmt = SqliteStatement(dbLock, "PRAGMA integrity_check"); |
| 147 | + while (stmt.step()) { |
| 148 | + errors.push_back(stmt.columnBlob(0).str()); |
| 149 | + } |
| 150 | + } |
| 151 | + auto runtimeInSeconds = |
| 152 | + std::chrono::duration<double>{integrityCheckRuntime.elapsed()}.count(); |
| 153 | + |
| 154 | + if (errors.empty() || (errors.size() == 1 && errors.front() == "ok")) { |
| 155 | + logger->logEvent(SqliteIntegrityCheck{runtimeInSeconds, 0}); |
| 156 | + return db; |
| 157 | + } else { |
| 158 | + logger->logEvent(SqliteIntegrityCheck{ |
| 159 | + runtimeInSeconds, folly::to_signed(errors.size())}); |
| 160 | + if (folly::kIsWindows) { |
| 161 | + XLOG(WARN) << "SqliteDatabase is corrupted"; |
| 162 | + for (auto& error : errors) { |
| 163 | + XLOG(WARN) << "Sqlite error: " << error; |
| 164 | + } |
| 165 | + db.reset(); |
| 166 | + return removeAndRecreateDb(path); |
| 167 | + } else { |
| 168 | + throw_<std::runtime_error>("SqliteDatabase is corrupted"); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + } catch (const std::exception& ex) { |
| 173 | + if (folly::kIsWindows) { |
| 174 | + XLOG(WARN) << "SqliteDatabase (" << path |
| 175 | + << ") failed to open: " << ex.what(); |
| 176 | + return removeAndRecreateDb(path); |
| 177 | + } |
| 178 | + throw; |
| 179 | + } |
| 180 | +} |
| 181 | +} // namespace |
| 182 | + |
122 | 183 | SqliteTreeStore::SqliteTreeStore(
|
123 | 184 | AbsolutePathPiece path,
|
| 185 | + std::shared_ptr<StructuredLogger> logger, |
124 | 186 | SqliteTreeStore::SynchronousMode synchronous_mode) {
|
125 | 187 | ensureDirectoryExists(path);
|
126 | 188 |
|
127 |
| - db_ = std::make_unique<SqliteDatabase>(path + kTreeStorePath); |
| 189 | + AbsolutePath sqliteDbPath = path + kTreeStorePath; |
| 190 | + db_ = openAndVerifyDb(sqliteDbPath, std::move(logger)); |
128 | 191 |
|
129 | 192 | // Enable WAL for faster writes to the database. See also:
|
130 | 193 | // https://www.sqlite.org/wal.html
|
|
0 commit comments