diff --git a/SQLClient.h b/SQLClient.h index c2608a3..d714adb 100644 --- a/SQLClient.h +++ b/SQLClient.h @@ -383,14 +383,15 @@ SQLCLIENT_PRIVATE NSString *_user; /** The configured user */ NSMutableArray *_statements; /** Uncommitted statements */ /** - * Timestamp of last operation.
+ * Timestamp of completion of last operation.
* Maintained by -simpleExecute: -simpleQuery:recordType:listType: * and -cache:simpleQuery:recordType:listType: * Also set for a failed connection attempt, but not reported by the * -lastOperation method in that case. */ NSTimeInterval _lastOperation; - NSTimeInterval _duration; + NSTimeInterval _lastStart; /** Last op start or connect */ + NSTimeInterval _duration; /** Duration logging threshold */ unsigned int _debugging; /** The current debugging level */ GSCache *_cache; /** The cache for query results */ NSThread *_cacheThread; /** Thread for cache queries */ @@ -687,6 +688,14 @@ SQLCLIENT_PRIVATE */ - (NSDate*) lastOperation; +/** Compares the receiver with the other client to see which one has been + * inactive but connected for longest (if they are connected) and returns + * that instance.
+ * If neither is idle but connected, the method returns nil.
+ * In a tie, the method returns the other instance. + */ +- (SQLClient*) longestIdle: (SQLClient*)other; + /** * Return the database reference name for this instance (or nil). */ diff --git a/SQLClient.m b/SQLClient.m index e6f011e..2080afb 100644 --- a/SQLClient.m +++ b/SQLClient.m @@ -76,10 +76,11 @@ NSString * const SQLClientDidDisconnectNotification static NSNull *null = nil; static NSArray *queryModes = nil; static NSThread *mainThread = nil; -static Class NSStringClass = 0; -static Class NSArrayClass = 0; -static Class NSDateClass = 0; -static Class NSSetClass = 0; +static Class NSStringClass = Nil; +static Class NSArrayClass = Nil; +static Class NSDateClass = Nil; +static Class NSSetClass = Nil; +static Class SQLClientClass = Nil; @interface _ConcreteSQLRecord : SQLRecord { @@ -738,29 +739,34 @@ static unsigned int maxConnections = 8; + (void) initialize { - static id modes[1]; - - modes[0] = NSDefaultRunLoopMode; - queryModes = [[NSArray alloc] initWithObjects: modes count: 1]; - GSTickerTimeNow(); - [SQLRecord class]; // Force initialisation - if (0 == clientsHash) + if (Nil == SQLClientClass && [SQLClient class] == self) { - clientsHash = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0); - clientsMap = NSCreateMapTable(NSObjectMapKeyCallBacks, - NSNonRetainedObjectMapValueCallBacks, 0); - clientsLock = [NSRecursiveLock new]; - beginStatement = [[NSArray arrayWithObject: beginString] retain]; - commitStatement = [[NSArray arrayWithObject: commitString] retain]; - rollbackStatement = [[NSArray arrayWithObject: rollbackString] retain]; - NSStringClass = [NSString class]; - NSArrayClass = [NSArray class]; - NSSetClass = [NSSet class]; - [NSTimer scheduledTimerWithTimeInterval: 1.0 - target: self - selector: @selector(_tick:) - userInfo: 0 - repeats: YES]; + static id modes[1]; + + SQLClientClass = self; + modes[0] = NSDefaultRunLoopMode; + queryModes = [[NSArray alloc] initWithObjects: modes count: 1]; + GSTickerTimeNow(); + [SQLRecord class]; // Force initialisation + if (0 == clientsHash) + { + clientsHash = NSCreateHashTable(NSNonOwnedPointerHashCallBacks, 0); + clientsMap = NSCreateMapTable(NSObjectMapKeyCallBacks, + NSNonRetainedObjectMapValueCallBacks, 0); + clientsLock = [NSRecursiveLock new]; + beginStatement = [[NSArray arrayWithObject: beginString] retain]; + commitStatement = [[NSArray arrayWithObject: commitString] retain]; + rollbackStatement + = [[NSArray arrayWithObject: rollbackString] retain]; + NSStringClass = [NSString class]; + NSArrayClass = [NSArray class]; + NSSetClass = [NSSet class]; + [NSTimer scheduledTimerWithTimeInterval: 1.0 + target: self + selector: @selector(_tick:) + userInfo: 0 + repeats: YES]; + } } } @@ -958,6 +964,7 @@ static unsigned int maxConnections = 8; } } + _lastStart = GSTickerTimeNow(); [self backendConnect]; /* On establishng a new connection, we must restore any * listen instructions in the backend. @@ -1274,6 +1281,51 @@ static unsigned int maxConnections = 8; return nil; } +- (SQLClient*) longestIdle: (SQLClient*)other +{ + NSTimeInterval t0; + NSTimeInterval t1; + + NSAssert([other isKindOfClass: SQLClientClass], NSInvalidArgumentException); + + t0 = _lastOperation; + if (t0 < _lastStart) + { + t0 = _lastStart; + } + if (NO == connected || 0 != _connectFails) + { + t0 = 0.0; + } + + if (YES == [other isProxy]) + { + t1 = 0.0; + } + else + { + t1 = other->_lastOperation; + if (t1 < other->_lastStart) + { + t1 = other->_lastStart; + } + if (NO == connected || 0 != other->_connectFails) + { + t1 = 0.0; + } + } + + if (t0 <= 0.0 && t1 <= 0.0) + { + return nil; + } + if (t1 <= t0) + { + return other; + } + return self; +} + - (NSString*) name { return _name; @@ -1706,7 +1758,6 @@ static unsigned int maxConnections = 8; [lock lock]; NS_DURING { - NSTimeInterval start = 0.0; NSString *statement; BOOL isCommit = NO; BOOL isRollback = NO; @@ -1722,10 +1773,7 @@ static unsigned int maxConnections = 8; isRollback = YES; } - if (_duration >= 0) - { - start = GSTickerTimeNow(); - } + _lastStart = GSTickerTimeNow(); result = [self backendExecute: info]; _lastOperation = GSTickerTimeNow(); [_statements addObject: statement]; @@ -1733,7 +1781,7 @@ static unsigned int maxConnections = 8; { NSTimeInterval d; - d = _lastOperation - start; + d = _lastOperation - _lastStart; if (d >= _duration) { if (isCommit || isRollback) @@ -1814,19 +1862,14 @@ static unsigned int maxConnections = 8; [lock lock]; NS_DURING { - NSTimeInterval start = 0.0; - - if (_duration >= 0) - { - start = GSTickerTimeNow(); - } + _lastStart = GSTickerTimeNow(); result = [self backendQuery: stmt recordType: rtype listType: ltype]; _lastOperation = GSTickerTimeNow(); if (_duration >= 0) { NSTimeInterval d; - d = _lastOperation - start; + d = _lastOperation - _lastStart; if (d >= _duration) { debug = [NSString stringWithFormat: @@ -2678,7 +2721,6 @@ static unsigned int maxConnections = 8; { NSMutableArray *result; NSMutableDictionary *md; - NSTimeInterval start; GSCache *c; id toCache; @@ -2688,7 +2730,7 @@ static unsigned int maxConnections = 8; md = [[NSThread currentThread] threadDictionary]; [md setObject: rtype forKey: @"SQLClientRecordType"]; [md setObject: ltype forKey: @"SQLClientListType"]; - start = GSTickerTimeNow(); + _lastStart = GSTickerTimeNow(); c = [self cache]; toCache = nil; @@ -2733,7 +2775,7 @@ static unsigned int maxConnections = 8; { NSTimeInterval d; - d = _lastOperation - start; + d = _lastOperation - _lastStart; if (d >= _duration) { [self debug: @"Duration %g for query %@", d, stmt]; diff --git a/testPostgres.m b/testPostgres.m index 0b221e2..88ceef0 100644 --- a/testPostgres.m +++ b/testPostgres.m @@ -69,11 +69,13 @@ main() nil] ]; - sp = [[SQLClientPool alloc] initWithConfiguration: nil - name: @"test" - max: 2 - min: 1]; - db = [[sp autorelease] provideClient]; + sp = [[[SQLClientPool alloc] initWithConfiguration: nil + name: @"test" + max: 2 + min: 1] autorelease]; + db = [sp provideClient]; + [sp swallowClient: db]; + db = [sp provideClient]; l = [Logger new]; [[NSNotificationCenter defaultCenter] addObserver: l