Skip to content

Commit 1621289

Browse files
committed
[Core] Lazily open and automatically close SQLite DB connections
Keeping the DB open grabs exclusive locks that prevents other processes from accessing the DB, even when we are otherwise not using it. This changes the driver to close the connection whenever a build has been completed. rdar://problem/42247751
1 parent ce315f4 commit 1621289

File tree

2 files changed

+80
-22
lines changed

2 files changed

+80
-22
lines changed

lib/Core/SQLiteBuildDB.cpp

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ class SQLiteBuildDB : public BuildDB {
7373
/// * 4: Pre-history
7474
static const int currentSchemaVersion = 8;
7575

76+
std::string path;
77+
uint32_t clientSchemaVersion;
78+
7679
sqlite3 *db = nullptr;
7780

7881
/// The mutex to protect all access to the database and statements.
@@ -98,16 +101,10 @@ class SQLiteBuildDB : public BuildDB {
98101
return out;
99102
}
100103

101-
public:
102-
virtual ~SQLiteBuildDB() {
103-
if (db)
104-
close();
105-
}
106-
107-
bool open(StringRef path, uint32_t clientSchemaVersion,
108-
std::string *error_out) {
109-
std::lock_guard<std::mutex> guard(dbMutex);
110-
assert(!db);
104+
bool open(std::string *error_out) {
105+
// The db is opened lazily whenever an operation on it occurs. Thus if it is
106+
// already open, we don't need to do any further work.
107+
if (db) return true;
111108

112109
// Configure SQLite3 on first use.
113110
//
@@ -124,7 +121,7 @@ class SQLiteBuildDB : public BuildDB {
124121
}
125122
}
126123

127-
int result = sqlite3_open(path.str().c_str(), &db);
124+
int result = sqlite3_open(path.c_str(), &db);
128125
if (result != SQLITE_OK) {
129126
*error_out = "unable to open database: " + std::string(
130127
sqlite3_errstr(result));
@@ -166,7 +163,7 @@ class SQLiteBuildDB : public BuildDB {
166163
sqlite3_close(db);
167164

168165
// Always recreate the database from scratch when the schema changes.
169-
result = basic::sys::unlink(path.str().c_str());
166+
result = basic::sys::unlink(path.c_str());
170167
if (result == -1) {
171168
if (errno != ENOENT) {
172169
*error_out = std::string("unable to unlink existing database: ") +
@@ -176,7 +173,7 @@ class SQLiteBuildDB : public BuildDB {
176173
}
177174
} else {
178175
// If the remove was successful, reopen the database.
179-
int result = sqlite3_open(path.str().c_str(), &db);
176+
int result = sqlite3_open(path.c_str(), &db);
180177
if (result != SQLITE_OK) {
181178
*error_out = getCurrentErrorMessage();
182179
return false;
@@ -285,22 +282,38 @@ class SQLiteBuildDB : public BuildDB {
285282
}
286283

287284
void close() {
288-
std::lock_guard<std::mutex> guard(dbMutex);
289-
assert(db);
285+
if (!db) return;
290286

291287
// Destroy prepared statements.
292288
sqlite3_finalize(findKeyIDForKeyStmt);
289+
findKeyIDForKeyStmt = nullptr;
293290
sqlite3_finalize(findKeyNameForKeyIDStmt);
291+
findKeyNameForKeyIDStmt = nullptr;
294292
sqlite3_finalize(findRuleResultStmt);
293+
findRuleResultStmt = nullptr;
295294
sqlite3_finalize(fastFindRuleResultStmt);
295+
fastFindRuleResultStmt = nullptr;
296296
sqlite3_finalize(deleteFromKeysStmt);
297+
deleteFromKeysStmt = nullptr;
297298
sqlite3_finalize(insertIntoKeysStmt);
299+
insertIntoKeysStmt = nullptr;
298300
sqlite3_finalize(insertIntoRuleResultsStmt);
301+
insertIntoRuleResultsStmt = nullptr;
299302

300303
sqlite3_close(db);
301304
db = nullptr;
302305
}
303306

307+
public:
308+
SQLiteBuildDB(StringRef path, uint32_t clientSchemaVersion)
309+
: path(path), clientSchemaVersion(clientSchemaVersion) { }
310+
311+
virtual ~SQLiteBuildDB() {
312+
std::lock_guard<std::mutex> guard(dbMutex);
313+
if (db)
314+
close();
315+
}
316+
304317
/// @name BuildDB API
305318
/// @{
306319

@@ -310,7 +323,11 @@ class SQLiteBuildDB : public BuildDB {
310323

311324
virtual uint64_t getCurrentIteration(bool* success_out, std::string *error_out) override {
312325
std::lock_guard<std::mutex> guard(dbMutex);
313-
assert(db);
326+
327+
if (!open(error_out)) {
328+
*success_out = false;
329+
return 0;
330+
}
314331

315332
// Fetch the iteration from the info table.
316333
sqlite3_stmt* stmt;
@@ -342,6 +359,11 @@ class SQLiteBuildDB : public BuildDB {
342359

343360
virtual bool setCurrentIteration(uint64_t value, std::string *error_out) override {
344361
std::lock_guard<std::mutex> guard(dbMutex);
362+
363+
if (!open(error_out)) {
364+
return false;
365+
}
366+
345367
sqlite3_stmt* stmt;
346368
int result;
347369
result = sqlite3_prepare_v2(
@@ -388,6 +410,10 @@ class SQLiteBuildDB : public BuildDB {
388410
std::lock_guard<std::mutex> guard(dbMutex);
389411
assert(result_out->builtAt == 0);
390412

413+
if (!open(error_out)) {
414+
return false;
415+
}
416+
391417
// Fetch the basic rule information.
392418
int result;
393419
int numDependencyBytes = 0;
@@ -523,6 +549,10 @@ class SQLiteBuildDB : public BuildDB {
523549
std::lock_guard<std::mutex> guard(dbMutex);
524550
int result;
525551

552+
if (!open(error_out)) {
553+
return false;
554+
}
555+
526556
auto dbKeyID = getKeyID(keyID, error_out);
527557
if (!error_out->empty()) {
528558
return false;
@@ -582,6 +612,9 @@ class SQLiteBuildDB : public BuildDB {
582612
virtual bool buildStarted(std::string *error_out) override {
583613
std::lock_guard<std::mutex> guard(dbMutex);
584614

615+
if (!open(error_out))
616+
return false;
617+
585618
// Execute the entire build inside a single transaction.
586619
//
587620
// FIXME: We should revist this, as we probably wouldn't want a crash in the
@@ -603,6 +636,10 @@ class SQLiteBuildDB : public BuildDB {
603636
int result = sqlite3_exec(db, "END;", nullptr, nullptr, nullptr);
604637
assert(result == SQLITE_OK);
605638
(void)result;
639+
640+
// We close the connection whenever a build completes so that we release
641+
// any locks that we may have on the file.
642+
close();
606643
}
607644

608645
/// @}
@@ -740,10 +777,7 @@ if (result != SQLITE_OK) { \
740777
std::unique_ptr<BuildDB> core::createSQLiteBuildDB(StringRef path,
741778
uint32_t clientSchemaVersion,
742779
std::string* error_out) {
743-
auto db = llvm::make_unique<SQLiteBuildDB>();
744-
if (!db->open(path, clientSchemaVersion, error_out))
745-
return nullptr;
746-
780+
auto db = llvm::make_unique<SQLiteBuildDB>(path, clientSchemaVersion);
747781
return std::move(db);
748782
}
749783

unittests/Core/SQLiteBuildDBTest.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,23 @@ TEST(SQLiteBuildDBTest, ErrorHandling) {
4040
sqlite3_exec(db, "PRAGMA locking_mode = EXCLUSIVE; BEGIN EXCLUSIVE;", nullptr, nullptr, nullptr);
4141

4242
buildDB = createSQLiteBuildDB(dbPath, 1, &error);
43-
EXPECT_TRUE(buildDB == nullptr);
43+
EXPECT_FALSE(buildDB == nullptr);
44+
45+
// The database is opened lazily, thus run an operation that will cause it
46+
// to be opened and verify that it fails as expected.
47+
bool result = true;
48+
buildDB->getCurrentIteration(&result, &error);
49+
EXPECT_FALSE(result);
50+
4451
std::stringstream out;
4552
out << "error: accessing build database \"" << path << "\": database is locked Possibly there are two concurrent builds running in the same filesystem location.";
4653
EXPECT_EQ(error, out.str());
4754

55+
// Clean up database connections before unlinking
56+
sqlite3_exec(db, "END;", nullptr, nullptr, nullptr);
57+
sqlite3_close(db);
58+
buildDB = nullptr;
59+
4860
ec = llvm::sys::fs::remove(dbPath.str());
4961
EXPECT_EQ(bool(ec), false);
5062
}
@@ -79,9 +91,21 @@ TEST(SQLiteBuildDBTest, LockedWhileBuilding) {
7991

8092
// Tests that we cannot create new connections while a build is running
8193
std::unique_ptr<BuildDB> otherBuildDB = createSQLiteBuildDB(dbPath, 1, &error);
82-
EXPECT_TRUE(otherBuildDB == nullptr);
94+
EXPECT_FALSE(otherBuildDB == nullptr);
95+
96+
// The database is opened lazily, thus run an operation that will cause it
97+
// to be opened and verify that it fails as expected.
98+
bool success = true;
99+
otherBuildDB->getCurrentIteration(&success, &error);
100+
EXPECT_FALSE(success);
83101
EXPECT_EQ(error, out.str());
84102

103+
// Clean up database connections before unlinking
104+
buildDB->buildComplete();
105+
buildDB = nullptr;
106+
secondBuildDB = nullptr;
107+
otherBuildDB = nullptr;
108+
85109
ec = llvm::sys::fs::remove(dbPath.str());
86110
EXPECT_EQ(bool(ec), false);
87111
}

0 commit comments

Comments
 (0)