Skip to content

Commit 6b71469

Browse files
committed
Fix Makefile conflicts
2 parents e169a44 + 2073105 commit 6b71469

20 files changed

+200
-508
lines changed

.github/workflows/bedrock.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121

2222
- name: Install the Mold Linker
2323
uses: rui314/setup-mold@v1
24-
24+
2525
- name: Checkout Bedrock
2626
uses: actions/[email protected]
2727

@@ -32,7 +32,7 @@ jobs:
3232
shell: bash
3333

3434
- name: Set up cache
35-
uses: actions/cache@v4.0.0
35+
uses: actions/cache@v4.2.0
3636
with:
3737
path: |-
3838
${{ env.CCACHE_BASEDIR }}

BedrockCommand.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ const string& BedrockCommand::getName() const {
5757
return defaultPluginName;
5858
}
5959

60+
const BedrockPlugin* BedrockCommand::getPlugin() const {
61+
return _plugin;
62+
}
63+
6064
int64_t BedrockCommand::_getTimeout(const SData& request, const uint64_t scheduledTime) {
6165
// Timeout is the default, unless explicitly supplied, or if Connection: forget is set.
6266
int64_t timeout = DEFAULT_TIMEOUT;

BedrockCommand.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class BedrockCommand : public SQLiteCommand {
8585
// Return the name of the plugin for this command.
8686
const string& getName() const;
8787

88+
const BedrockPlugin* getPlugin() const;
89+
8890
// Take all of the HTTPS requests attached to this object, and serialize them to a string.
8991
string serializeHTTPSRequests();
9092

@@ -99,7 +101,7 @@ class BedrockCommand : public SQLiteCommand {
99101
}
100102

101103
// Bedrock will call this before writing to the database after it has prepared a transaction for each plugin to allow it to
102-
// enable a handler function for prepare If a plugin would like to perform operations after prepare but before commit, this should
104+
// enable a handler function for prepare If a plugin would like to perform operations after prepare but before commit, this should
103105
// return true, and it should set the prepareHandler it would like to use.
104106
virtual bool shouldEnableOnPrepareNotification(const SQLite& db, void (**onPrepareHandler)(SQLite& _db, int64_t tableID)) {
105107
return false;

BedrockCore.cpp

+11-9
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ uint64_t BedrockCore::_getRemainingTime(const unique_ptr<BedrockCommand>& comman
5252
return isProcessing ? min(processTimeout, adjustedTimeout) : adjustedTimeout;
5353
}
5454

55-
bool BedrockCore::isTimedOut(unique_ptr<BedrockCommand>& command) {
55+
bool BedrockCore::isTimedOut(unique_ptr<BedrockCommand>& command, SQLite* db, const BedrockServer* server) {
5656
try {
5757
_getRemainingTime(command, false);
5858
} catch (const SException& e) {
5959
// Yep, timed out.
60-
_handleCommandException(command, e);
60+
_handleCommandException(command, e, db, server);
6161
command->complete = true;
6262
return true;
6363
}
@@ -104,7 +104,7 @@ void BedrockCore::prePeekCommand(unique_ptr<BedrockCommand>& command, bool isBlo
104104
STHROW("555 Timeout prePeeking command");
105105
}
106106
} catch (const SException& e) {
107-
_handleCommandException(command, e);
107+
_handleCommandException(command, e, &_db, &_server);
108108
command->complete = true;
109109
} catch (...) {
110110
SALERT("Unhandled exception typename: " << SGetCurrentExceptionName() << ", command: " << request.methodLine);
@@ -186,7 +186,7 @@ BedrockCore::RESULT BedrockCore::peekCommand(unique_ptr<BedrockCommand>& command
186186
}
187187
} catch (const SException& e) {
188188
command->repeek = false;
189-
_handleCommandException(command, e);
189+
_handleCommandException(command, e, &_db, &_server);
190190
} catch (const SHTTPSManager::NotLeading& e) {
191191
command->repeek = false;
192192
returnValue = RESULT::SHOULD_PROCESS;
@@ -284,7 +284,7 @@ BedrockCore::RESULT BedrockCore::processCommand(unique_ptr<BedrockCommand>& comm
284284
}
285285
}
286286
} catch (const SException& e) {
287-
_handleCommandException(command, e);
287+
_handleCommandException(command, e, &_db, &_server);
288288
_db.rollback();
289289
needsCommit = false;
290290
} catch (const SQLite::constraint_error& e) {
@@ -353,7 +353,7 @@ void BedrockCore::postProcessCommand(unique_ptr<BedrockCommand>& command, bool i
353353
STHROW("555 Timeout postProcessing command");
354354
}
355355
} catch (const SException& e) {
356-
_handleCommandException(command, e);
356+
_handleCommandException(command, e, &_db, &_server);
357357
} catch (...) {
358358
SALERT("Unhandled exception typename: " << SGetCurrentExceptionName() << ", command: " << request.methodLine);
359359
command->response.methodLine = "500 Unhandled Exception";
@@ -367,7 +367,7 @@ void BedrockCore::postProcessCommand(unique_ptr<BedrockCommand>& command, bool i
367367
_db.setQueryOnly(false);
368368
}
369369

370-
void BedrockCore::_handleCommandException(unique_ptr<BedrockCommand>& command, const SException& e) {
370+
void BedrockCore::_handleCommandException(unique_ptr<BedrockCommand>& command, const SException& e, SQLite* db, const BedrockServer* server) {
371371
string msg = "Error processing command '" + command->request.methodLine + "' (" + e.what() + "), ignoring.";
372372
if (!e.body.empty()) {
373373
msg = msg + " Request body: " + e.body;
@@ -396,9 +396,11 @@ void BedrockCore::_handleCommandException(unique_ptr<BedrockCommand>& command, c
396396
}
397397

398398
// Add the commitCount header to the response.
399-
command->response["commitCount"] = to_string(_db.getCommitCount());
399+
if (db) {
400+
command->response["commitCount"] = to_string(db->getCommitCount());
401+
}
400402

401-
if (_server.args.isSet("-extraExceptionLogging")) {
403+
if (server && server->args.isSet("-extraExceptionLogging")) {
402404
auto stack = e.details();
403405
command->response["exceptionSource"] = stack.back();
404406
}

BedrockCore.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class BedrockCore : public SQLiteCore {
3434
// Checks if a command has already timed out. Like `peekCommand` without doing any work. Returns `true` and sets
3535
// the same command state as `peekCommand` would if the command has timed out. Returns `false` and does nothing if
3636
// the command hasn't timed out.
37-
bool isTimedOut(unique_ptr<BedrockCommand>& command);
37+
static bool isTimedOut(unique_ptr<BedrockCommand>& command, SQLite* db = nullptr, const BedrockServer* server = nullptr);
3838

3939
void prePeekCommand(unique_ptr<BedrockCommand>& command, bool isBlockingCommitThread);
4040

@@ -71,8 +71,8 @@ class BedrockCore : public SQLiteCore {
7171
// Gets the amount of time remaining until this command times out. This is the difference between the command's
7272
// 'timeout' value (or the default timeout, if not set) and the time the command was initially scheduled to run. If
7373
// this time is already expired, this throws `555 Timeout`
74-
uint64_t _getRemainingTime(const unique_ptr<BedrockCommand>& command, bool isProcessing);
74+
static uint64_t _getRemainingTime(const unique_ptr<BedrockCommand>& command, bool isProcessing);
7575

76-
void _handleCommandException(unique_ptr<BedrockCommand>& command, const SException& e);
76+
static void _handleCommandException(unique_ptr<BedrockCommand>& command, const SException& e, SQLite* db = nullptr, const BedrockServer* server = nullptr);
7777
const BedrockServer& _server;
7878
};

BedrockPlugin.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,7 @@ bool BedrockPlugin::preventAttach() {
6969
void BedrockPlugin::timerFired(SStopwatch* timer) {}
7070

7171
void BedrockPlugin::upgradeDatabase(SQLite& db) {}
72+
73+
bool BedrockPlugin::shouldLockCommitPageOnTableConflict(const string& tableName) const {
74+
return true;
75+
}

BedrockPlugin.h

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class BedrockPlugin {
7878
// Called when the sync thread is finishing, before destroying DB handles.
7979
virtual void serverStopping() {}
8080

81+
// Should a conflict on the given tableName result in locking the associated database page when we try to commit again?
82+
virtual bool shouldLockCommitPageOnTableConflict(const string& tableName) const;
83+
8184
// Map of plugin names to functions that will return a new plugin of the given type.
8285
static map<string, function<BedrockPlugin*(BedrockServer&)>> g_registeredPluginList;
8386

BedrockServer.cpp

+18-5
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,14 @@ void BedrockServer::runCommand(unique_ptr<BedrockCommand>&& _command, bool isBlo
801801
// We just spin until the node looks ready to go. Typically, this doesn't happen expect briefly at startup.
802802
size_t waitCount = 0;
803803
while (_upgradeInProgress || (getState() != SQLiteNodeState::LEADING && getState() != SQLiteNodeState::FOLLOWING)) {
804+
805+
// It's feasible that our command times out in this loop. In this case, we do not have a DB object to pass.
806+
// The only implication of this is the response does not get the commitCount attached to it.
807+
if (BedrockCore::isTimedOut(command, nullptr, this)) {
808+
_reply(command);
809+
return;
810+
}
811+
804812
// This sleep call is pretty ugly, but it should almost never happen. We're accepting the potential
805813
// looping sleep call for the general case where we just check some bools and continue, instead of
806814
// avoiding the sleep call but having every thread lock a mutex here on every loop.
@@ -880,7 +888,7 @@ void BedrockServer::runCommand(unique_ptr<BedrockCommand>&& _command, bool isBlo
880888
// to be returned to the main queue, where they would have timed out in `peek`, but it was never called
881889
// because the commands already had a HTTPS request attached, and then they were immediately re-sent to the
882890
// sync queue, because of the QUORUM consistency requirement, resulting in an endless loop.
883-
if (core.isTimedOut(command)) {
891+
if (core.isTimedOut(command, &db, this)) {
884892
_reply(command);
885893
return;
886894
}
@@ -1030,8 +1038,6 @@ void BedrockServer::runCommand(unique_ptr<BedrockCommand>&& _command, bool isBlo
10301038
// loop and send it to followers. NOTE: we don't check for null here, that should be
10311039
// impossible inside a worker thread.
10321040
_syncNode->notifyCommit();
1033-
SINFO("Committed leader transaction #" << transactionID << "(" << transactionHash << "). Command: '" << command->request.methodLine << "', blocking: "
1034-
<< (isBlocking ? "true" : "false"));
10351041
_conflictManager.recordTables(command->request.methodLine, db.getTablesUsed());
10361042
// So we must still be leading, and at this point our commit has succeeded, let's
10371043
// mark it as complete. We add the currentCommit count here as well.
@@ -1043,9 +1049,10 @@ void BedrockServer::runCommand(unique_ptr<BedrockCommand>&& _command, bool isBlo
10431049
lastConflictTable = db.getLastConflictTable();
10441050

10451051
// Journals are always chosen at the time of commit. So in case there was a conflict on the journal in
1046-
// the previous commit, the chances are very low (1/192) that we'll choose the same journal, thus, we
1052+
// the previous commit, the chances are very low that we'll choose the same journal, thus, we
10471053
// don't need to lock our next commit on this page conflict.
1048-
if (!SStartsWith(lastConflictTable, "journal")) {
1054+
// Plugins may define other tables on which we should not lock our next commit.
1055+
if (!SStartsWith(lastConflictTable, "journal") && (command->getPlugin() == nullptr || command->getPlugin()->shouldLockCommitPageOnTableConflict(lastConflictTable))) {
10491056
lastConflictPage = db.getLastConflictPage();
10501057
}
10511058
}
@@ -1164,6 +1171,12 @@ bool BedrockServer::_wouldCrash(const unique_ptr<BedrockCommand>& command) {
11641171
return false;
11651172
}
11661173

1174+
// If this command crashed with more than one set of identifying values, it means
1175+
// we've already crashed more than one node. Let's fully block this command in that case.
1176+
if (commandIt->second.size() > 1) {
1177+
return true;
1178+
}
1179+
11671180
// Look at each crash-inducing command that has the same methodLine.
11681181
for (const STable& values : commandIt->second) {
11691182

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ INCLUDE = -I$(PROJECT) -I$(PROJECT)/mbedtls/include
1515
CXXFLAGS = -g -std=c++20 -fPIC -DSQLITE_ENABLE_NORMALIZE $(BEDROCK_OPTIM_COMPILE_FLAG) -Wall -Werror -Wformat-security -Wno-unqualified-std-cast-call -Wno-error=deprecated-declarations $(INCLUDE)
1616

1717
# Amalgamation flags
18-
AMALGAMATION_FLAGS = -Wno-unused-but-set-variable -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT -DSQLITE_ENABLE_NOOP_UPDATE -DSQLITE_MUTEX_ALERT_MILLISECONDS=20 -DHAVE_USLEEP=1 -DSQLITE_MAX_MMAP_SIZE=17592186044416ull -DSQLITE_SHARED_MAPPING -DSQLITE_ENABLE_NORMALIZE -DSQLITE_MAX_PAGE_COUNT=4294967294 -DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS -DSQLITE_DEFAULT_CACHE_SIZE=-51200 -DSQLITE_WAL_BIGHASH
18+
AMALGAMATION_FLAGS = -Wno-unused-but-set-variable -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT -DSQLITE_ENABLE_NOOP_UPDATE -DSQLITE_MUTEX_ALERT_MILLISECONDS=20 -DHAVE_USLEEP=1 -DSQLITE_MAX_MMAP_SIZE=17592186044416ull -DSQLITE_SHARED_MAPPING -DSQLITE_ENABLE_NORMALIZE -DSQLITE_MAX_PAGE_COUNT=4294967294 -DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS -DSQLITE_DEFAULT_CACHE_SIZE=-51200 -DSQLITE_MAX_FUNCTION_ARG=32767 -DSQLITE_WAL_BIGHASH
1919

2020
# All our intermediate, dependency, object, etc files get hidden in here.
2121
INTERMEDIATEDIR = .build

libstuff/libstuff.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -2722,7 +2722,6 @@ int SQuery(sqlite3* db, const char* e, const string& sql, SQResult& result, int6
27222722

27232723
// But we log for commit conflicts as well, to keep track of how often this happens with this experimental feature.
27242724
if (extErr == SQLITE_BUSY_SNAPSHOT) {
2725-
SHMMM("[concurrent] commit conflict.");
27262725
return extErr;
27272726
}
27282727
return error;

sqlitecluster/SQLite.cpp

+3-13
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ bool SQLite::beginTransaction(TRANSACTION_TYPE type) {
393393
// We actively track transaction counts incrementing and decrementing to log the number of active open transactions at any given moment.
394394
_sharedData.openTransactionCount++;
395395

396-
SINFO("[concurrent] Beginning transaction - open transaction count: " << (_sharedData.openTransactionCount));
396+
SINFO("Beginning transaction - open transaction count: " << (_sharedData.openTransactionCount));
397397
uint64_t before = STimeNow();
398398
_insideTransaction = !SQuery(_db, "starting db transaction", "BEGIN CONCURRENT");
399399

@@ -663,7 +663,7 @@ bool SQLite::prepare(uint64_t* transactionID, string* transactionhash) {
663663
SQResult journalLookupResult;
664664
SASSERT(!SQuery(_db, "getting commit min", "SELECT MIN(id) FROM " + _journalName, journalLookupResult));
665665
uint64_t minJournalEntry = journalLookupResult.size() ? SToUInt64(journalLookupResult[0][0]) : 0;
666-
666+
667667
// Note that this can change before we hold the lock on _sharedData.commitLock, but it doesn't matter yet, as we're only
668668
// using it to truncate the journal. We'll reset this value once we acquire that lock.
669669
uint64_t commitCount = _sharedData.commitCount;
@@ -677,13 +677,9 @@ bool SQLite::prepare(uint64_t* transactionID, string* transactionhash) {
677677
// where this journal in particular has accumulated a large backlog.
678678
static const size_t deleteLimit = 10;
679679
if (minJournalEntry < oldestCommitToKeep) {
680-
auto startUS = STimeNow();
681680
shared_lock<shared_mutex> lock(_sharedData.writeLock);
682681
string query = "DELETE FROM " + _journalName + " WHERE id < " + SQ(oldestCommitToKeep) + " LIMIT " + SQ(deleteLimit);
683682
SASSERT(!SQuery(_db, "Deleting oldest journal rows", query));
684-
size_t deletedCount = sqlite3_changes(_db);
685-
SINFO("Removed " << deletedCount << " rows from journal " << _journalName << ", oldestToKeep: " << oldestCommitToKeep << ", count:"
686-
<< commitCount << ", limit: " << _maxJournalSize << ", in " << (STimeNow() - startUS) << "us.");
687683
}
688684

689685
// We lock this here, so that we can guarantee the order in which commits show up in the database.
@@ -771,9 +767,6 @@ int SQLite::commit(const string& description, function<void()>* preCheckpointCal
771767
result = SQuery(_db, "committing db transaction", "COMMIT");
772768
_lastConflictPage = _conflictPage;
773769
_lastConflictTable = _conflictTable;
774-
if (_lastConflictPage) {
775-
SINFO(format("part of last conflict page: {}, conflict table: {}", _conflictPage, _conflictTable));
776-
}
777770

778771
// If there were conflicting commits, will return SQLITE_BUSY_SNAPSHOT
779772
SASSERT(result == SQLITE_OK || result == SQLITE_BUSY_SNAPSHOT);
@@ -834,7 +827,7 @@ int SQLite::commit(const string& description, function<void()>* preCheckpointCal
834827
_lastConflictPage = 0;
835828
_lastConflictTable = "";
836829
} else {
837-
SINFO("Commit failed, waiting for rollback.");
830+
// The commit failed, we will rollback.
838831
}
839832

840833
// if we got SQLITE_BUSY_SNAPSHOT, then we're *still* holding commitLock, and it will need to be unlocked by
@@ -883,9 +876,6 @@ void SQLite::rollback() {
883876
// Finally done with this.
884877
_insideTransaction = false;
885878
_uncommittedHash.clear();
886-
if (_uncommittedQuery.size()) {
887-
SINFO("Rollback successful.");
888-
}
889879
_uncommittedQuery.clear();
890880

891881
// Only unlock the mutex if we've previously locked it. We can call `rollback` to cancel a transaction without

sqlitecluster/SQLiteCore.h

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
class SQLite;
33
class SQLiteNode;
44

5+
#include <cstdint>
6+
#include <string>
7+
8+
using namespace std;
9+
510
class SQLiteCore {
611
public:
712
// Constructor that stores the database object we'll be working on.

0 commit comments

Comments
 (0)