Skip to content

Commit ea0eb89

Browse files
authored
Introduce option to checkpoint database on Flush() (#1104)
The way SQLite is initialized (synchronous=NORMAL config) introduces potential issues with durability of changes/updates in some corner cases. iOS Outlook logs telemetry in scenarios where some critical validation/check failed and cannot be handled and process self-terminates. In between logging the event and terminating we call Flush() to avoid lost events. If we checkpoint the DB before returning from the Flush() call app can safely assume no events will be lost. This change introduces an option that enables such behavior. Alternative solution would be to use the default configuration (with synchronous=FULL) which doesn't have that problem but has overhead of filesystem syncing for each transaction which makes it a bad idea from the perf point of view.
1 parent 8f6875c commit ea0eb89

8 files changed

+56
-1
lines changed

lib/include/public/ILogConfiguration.hpp

+5
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ namespace MAT_NS_BEGIN
144144
/// </summary>
145145
static constexpr const char* const CFG_INT_RAM_QUEUE_BUFFERS = "maxDBFlushQueues";
146146

147+
/// <summary>
148+
/// SQLite DB will be checkpointed when flushing.
149+
/// </summary>
150+
static constexpr const char* const CFG_BOOL_CHECKPOINT_DB_ON_FLUSH = "checkpointDBOnFlush";
151+
147152
/// <summary>
148153
/// The trace level mask.
149154
/// </summary>

lib/offline/ISqlite3Proxy.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ISqlite3Proxy {
5555
virtual const void* sqlite3_value_blob(sqlite3_value* value) = 0;
5656
virtual int sqlite3_value_bytes(sqlite3_value* value) = 0;
5757
virtual sqlite3_vfs* sqlite3_vfs_find(char const* zVfsName) = 0;
58+
virtual void sqlite3_wal_checkpoint(sqlite3* db) = 0;
5859
};
5960

6061
extern ISqlite3Proxy* g_sqlite3Proxy;

lib/offline/OfflineStorageHandler.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ namespace MAT_NS_BEGIN {
199199
}
200200
}
201201

202+
// Checkpoint DB
203+
if (m_config.HasConfig(CFG_BOOL_CHECKPOINT_DB_ON_FLUSH) && m_config[CFG_BOOL_CHECKPOINT_DB_ON_FLUSH])
204+
{
205+
m_offlineStorageDisk->Flush();
206+
}
207+
202208
m_isStorageFullNotificationSend = false;
203209

204210
// Flush is done, notify the waiters

lib/offline/OfflineStorage_SQLite.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ namespace MAT_NS_BEGIN {
134134
}
135135
}
136136

137+
void OfflineStorage_SQLite::Flush()
138+
{
139+
if (m_db)
140+
m_db->flush();
141+
}
142+
137143
void OfflineStorage_SQLite::Execute(std::string command)
138144
{
139145
if (m_db)

lib/offline/OfflineStorage_SQLite.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace MAT_NS_BEGIN {
3131
virtual ~OfflineStorage_SQLite() override;
3232
virtual void Initialize(IOfflineStorageObserver& observer) override;
3333
virtual void Shutdown() override;
34-
virtual void Flush() override {};
34+
virtual void Flush() override;
3535
virtual void Execute(std::string command);
3636
virtual bool StoreRecord(StorageRecord const& record) override;
3737
virtual size_t StoreRecords(std::vector<StorageRecord> & records) override;

lib/offline/SQLiteWrapper.hpp

+10
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ namespace MAT_NS_BEGIN {
184184
{
185185
return ::sqlite3_vfs_find(zVfsName);
186186
}
187+
188+
void sqlite3_wal_checkpoint(sqlite3* db) override
189+
{
190+
::sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_FULL, NULL, NULL);
191+
}
187192
} g_realSqlite3Proxy;
188193

189194
ISqlite3Proxy* g_sqlite3Proxy = &g_realSqlite3Proxy;
@@ -462,6 +467,11 @@ namespace MAT_NS_BEGIN {
462467
return records;
463468
}
464469

470+
void flush()
471+
{
472+
g_sqlite3Proxy->sqlite3_wal_checkpoint(m_db);
473+
}
474+
465475
protected:
466476

467477
static void sqliteFunc_tokenize(sqlite3_context* ctx, int argc, sqlite3_value** argv)

wrappers/obj-c/ODWLogConfiguration.h

+11
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,17 @@ extern NSString * _Nonnull const ODWCFG_BOOL_SESSION_RESET_ENABLED;
391391
*/
392392
+(nullable NSString *)cacheFilePath;
393393

394+
/*!
395+
@brief Controls if DB will be checkpointed when flushing
396+
@param enableDbCheckpointOnFlush True if DB should be checkpointed when flushing.
397+
*/
398+
+(void)setEnableDbCheckpointOnFlush:(bool)enableDbCheckpointOnFlush;
399+
400+
/*!
401+
@brief Returns true if DB will be checkpointed when flushing.
402+
*/
403+
+(bool)enableDbCheckpointOnFlush;
404+
394405
/*!
395406
@brief Sets a config key to a string value for the copied config
396407
@param key A key.

wrappers/obj-c/ODWLogConfiguration.mm

+16
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,22 @@ +(nullable NSString *)cacheFilePath
431431
return [NSString stringWithCString:strCacheFilePath.c_str() encoding:NSUTF8StringEncoding];
432432
}
433433

434+
+(void)setEnableDbCheckpointOnFlush:(bool)enabled
435+
{
436+
auto& config = LogManager::GetLogConfiguration();
437+
config[CFG_BOOL_CHECKPOINT_DB_ON_FLUSH] = enabled;
438+
}
439+
440+
+(bool)enableDbCheckpointOnFlush
441+
{
442+
auto& config = LogManager::GetLogConfiguration();
443+
if (config.HasConfig(CFG_BOOL_CHECKPOINT_DB_ON_FLUSH)) {
444+
return config[CFG_BOOL_CHECKPOINT_DB_ON_FLUSH];
445+
} else {
446+
return false;
447+
}
448+
}
449+
434450
-(void)set:(nonnull NSString *)key withValue:(nonnull NSString *)value
435451
{
436452
(*_wrappedConfiguration)[[key UTF8String]] = [value UTF8String];

0 commit comments

Comments
 (0)