diff options
author | August Mueller <gus@mp.local> | 2014-03-01 01:05:44 +0400 |
---|---|---|
committer | August Mueller <gus@mp.local> | 2014-03-01 01:05:44 +0400 |
commit | 484b046a4a5ba7e8c8d3bb87dc838e71fa26db42 (patch) | |
tree | e9687c506326e0550d854b867b8e93117589f162 | |
parent | 995dbb5540d3431decb10de6eff51a79c86527c8 (diff) |
Discovered sqlite3_busy_handler, and got to kill some ivars and code. busyTimeout has been replaced by setMaxBusyRetryTimeInterval: - this is a _lot_ better than adding loops everywhere.
-rw-r--r-- | Tests/FMDatabasePoolTests.m | 2 | ||||
-rw-r--r-- | Tests/FMDatabaseTests.m | 2 | ||||
-rw-r--r-- | src/FMDatabase.h | 11 | ||||
-rw-r--r-- | src/FMDatabase.m | 173 |
4 files changed, 89 insertions, 99 deletions
diff --git a/Tests/FMDatabasePoolTests.m b/Tests/FMDatabasePoolTests.m index 2100b31..bb759cf 100644 --- a/Tests/FMDatabasePoolTests.m +++ b/Tests/FMDatabasePoolTests.m @@ -254,7 +254,7 @@ - (BOOL)databasePool:(FMDatabasePool*)pool shouldAddDatabaseToPool:(FMDatabase*)database { - [database setRetryTimeout:.1]; + [database setMaxBusyRetryTimeInterval:10]; // [database setCrashOnErrors:YES]; #pragma message "FIXME: Gus - we need to check for a SQLITE_BUSY when we call sqlite3_step. sqlite will sleep for the retry amount - BUT, it won't just try the step again. We'll have to put the old loops back in. testReadWriteStressTest shows this mistake. Maybe add a private method to FMDatabase that does the stepping and loop for us, and replace sqlite3_step with that?" return YES; diff --git a/Tests/FMDatabaseTests.m b/Tests/FMDatabaseTests.m index 279f09f..66313d9 100644 --- a/Tests/FMDatabaseTests.m +++ b/Tests/FMDatabaseTests.m @@ -176,7 +176,7 @@ [self.db executeUpdate:@"create table t1 (a integer)"]; [self.db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithInt:5]]; - [self.db setRetryTimeout:2]; + [self.db setMaxBusyRetryTimeInterval:2]; FMDatabase *newDB = [FMDatabase databaseWithPath:self.databasePath]; [newDB open]; diff --git a/src/FMDatabase.h b/src/FMDatabase.h index 11e4cb5..8977c92 100644 --- a/src/FMDatabase.h +++ b/src/FMDatabase.h @@ -74,7 +74,8 @@ BOOL _shouldCacheStatements; BOOL _isExecutingStatement; BOOL _inTransaction; - NSTimeInterval _busyTimeout; + NSTimeInterval _maxBusyRetryTimeInterval; + NSTimeInterval _startBusyRetryTime; NSMutableDictionary *_cachedStatements; NSMutableSet *_openResultSets; @@ -95,10 +96,6 @@ @property (atomic, assign) BOOL checkedOut; -/** Busy retry timeout */ - -@property (atomic, assign) NSTimeInterval busyTimeout; - /** Crash on errors */ @property (atomic, assign) BOOL crashOnErrors; @@ -692,8 +689,8 @@ // description forthcoming -- (void)setRetryTimeout:(NSTimeInterval)timeout; -- (NSTimeInterval)retryTimeout; +- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeoutInSeconds; +- (NSTimeInterval)maxBusyRetryTimeInterval; #if SQLITE_VERSION_NUMBER >= 3007000 diff --git a/src/FMDatabase.m b/src/FMDatabase.m index d3736a2..d6d2b84 100644 --- a/src/FMDatabase.m +++ b/src/FMDatabase.m @@ -12,7 +12,6 @@ @synthesize cachedStatements=_cachedStatements; @synthesize logsErrors=_logsErrors; @synthesize crashOnErrors=_crashOnErrors; -@synthesize busyTimeout=_busyTimeout; @synthesize checkedOut=_checkedOut; @synthesize traceExecution=_traceExecution; @@ -40,12 +39,12 @@ self = [super init]; if (self) { - _databasePath = [aPath copy]; - _openResultSets = [[NSMutableSet alloc] init]; - _db = nil; - _logsErrors = YES; - _crashOnErrors = NO; - _busyTimeout = 0; + _databasePath = [aPath copy]; + _openResultSets = [[NSMutableSet alloc] init]; + _db = nil; + _logsErrors = YES; + _crashOnErrors = NO; + _maxBusyRetryTimeInterval = 2; } return self; @@ -102,8 +101,9 @@ return NO; } - if (_busyTimeout > 0.0) { - sqlite3_busy_timeout(_db, (int)(_busyTimeout * 1000)); + if (_maxBusyRetryTimeInterval > 0.0) { + // set the handler + [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval]; } @@ -122,8 +122,9 @@ return NO; } - if (_busyTimeout > 0.0) { - sqlite3_busy_timeout(_db, (int)(_busyTimeout * 1000)); + if (_maxBusyRetryTimeInterval > 0.0) { + // set the handler + [self setMaxBusyRetryTimeInterval:_maxBusyRetryTimeInterval]; } return YES; @@ -169,15 +170,44 @@ } -- (void)setRetryTimeout:(NSTimeInterval)timeout { - _busyTimeout = timeout; - if (_db) { - sqlite3_busy_timeout(_db, (int)(timeout * 1000)); +static int FMDatabaseBusyHandler(void *f, int count) { + + FMDatabase *self = (__bridge FMDatabase*)f; + + if (count == 0) { + self->_startBusyRetryTime = [NSDate timeIntervalSinceReferenceDate]; + return 1; + } + + NSTimeInterval delta = [NSDate timeIntervalSinceReferenceDate] - (self->_startBusyRetryTime); + + if (delta < [self maxBusyRetryTimeInterval]) { + sqlite3_sleep(50); // milliseconds + return 1; + } + + return 0; +} + +- (void)setMaxBusyRetryTimeInterval:(NSTimeInterval)timeout { + + _maxBusyRetryTimeInterval = timeout; + + if (!_db) { + return; + } + + if (timeout > 0) { + sqlite3_busy_handler(_db, &FMDatabaseBusyHandler, (__bridge void *)(self)); + } + else { + // turn it off otherwise + sqlite3_busy_handler(_db, nil, nil); } } -- (NSTimeInterval)retryTimeout { - return _busyTimeout; +- (NSTimeInterval)maxBusyRetryTimeInterval { + return _maxBusyRetryTimeInterval; } @@ -660,37 +690,26 @@ [statement reset]; } - - if (!pStmt) { + + rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); - BOOL retry; - - do { - retry = NO; - rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); - - /*if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { - retry = YES; + if (SQLITE_OK != rc) { + if (_logsErrors) { + NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); + NSLog(@"DB Query: %@", sql); + NSLog(@"DB Path: %@", _databasePath); } - else */if (SQLITE_OK != rc) { - if (_logsErrors) { - NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - NSLog(@"DB Query: %@", sql); - NSLog(@"DB Path: %@", _databasePath); - } - - if (_crashOnErrors) { - NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); - abort(); - } - - sqlite3_finalize(pStmt); - _isExecutingStatement = NO; - return nil; + + if (_crashOnErrors) { + NSAssert(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); + abort(); } - } while (retry); + sqlite3_finalize(pStmt); + _isExecutingStatement = NO; + return nil; + } } id obj; @@ -846,8 +865,6 @@ if (!pStmt) { rc = sqlite3_prepare_v2(_db, [sql UTF8String], -1, &pStmt, 0); - #pragma message "FIXME: need the busy loop here." - if (SQLITE_OK != rc) { if (_logsErrors) { NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); @@ -942,55 +959,31 @@ ** executed is not a SELECT statement, we assume no data will be returned. */ - BOOL retry; - NSInteger numberOfRetries = 0; + rc = sqlite3_step(pStmt); - do { - retry = NO; - rc = sqlite3_step(pStmt); - - if (SQLITE_BUSY == rc || SQLITE_LOCKED == rc) { - retry = YES; - - if (SQLITE_LOCKED == rc) { - rc = sqlite3_reset(pStmt); - if (rc != SQLITE_LOCKED) { - NSLog(@"Unexpected result from sqlite3_reset (%d) eu", rc); - } - } - -#pragma message "FIXME: HOW DO WE KNOW HOW TO BREAK OUT OF THIS? 10 is kind of a magic number?" - - if ((numberOfRetries++ > 10)) { - NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); - NSLog(@"Database busy"); - retry = NO; - } - } - else if (SQLITE_DONE == rc) { - // all is well, let's return. - } - else if (SQLITE_ERROR == rc) { - if (_logsErrors) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } + if (SQLITE_DONE == rc) { + // all is well, let's return. + } + else if (SQLITE_ERROR == rc) { + if (_logsErrors) { + NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(_db)); + NSLog(@"DB Query: %@", sql); } - else if (SQLITE_MISUSE == rc) { - // uh oh. - if (_logsErrors) { - NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } + } + else if (SQLITE_MISUSE == rc) { + // uh oh. + if (_logsErrors) { + NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(_db)); + NSLog(@"DB Query: %@", sql); } - else { - // wtf? - if (_logsErrors) { - NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db)); - NSLog(@"DB Query: %@", sql); - } + } + else { + // wtf? + if (_logsErrors) { + NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(_db)); + NSLog(@"DB Query: %@", sql); } - } while (retry); + } if (rc == SQLITE_ROW) { NSAssert(NO, @"A executeUpdate is being called with a query string '%@'", sql); |