aboutsummaryrefslogtreecommitdiffstats
path: root/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
diff options
context:
space:
mode:
authorrowanbeentje <rowan@beent.je>2010-07-14 23:56:30 +0000
committerrowanbeentje <rowan@beent.je>2010-07-14 23:56:30 +0000
commit31a094d621fc266b401c1f216a3b757638151651 (patch)
treef332a37ee260765ca9424e1ae256943e297f31b8 /Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
parent52011b886bc031b99a1a359fa17b5e5469e2046d (diff)
downloadsequelpro-31a094d621fc266b401c1f216a3b757638151651.tar.gz
sequelpro-31a094d621fc266b401c1f216a3b757638151651.tar.bz2
sequelpro-31a094d621fc266b401c1f216a3b757638151651.zip
- Clean up automatic reconnection code
- Allow keepalive pings to perform automatic reconnections after a few consecutive failures - Fix a number of keepalive ping bugs, including compatibility with the new threaded connection methods and reconnection timing issues causing keepalive pings to not fire correctly
Diffstat (limited to 'Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m')
-rw-r--r--Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m75
1 files changed, 49 insertions, 26 deletions
diff --git a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
index e8ba9930..143152d2 100644
--- a/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
+++ b/Frameworks/MCPKit/MCPFoundationKit/MCPConnection.m
@@ -43,6 +43,7 @@
BOOL lastPingSuccess;
BOOL pingActive;
+NSInteger pingFailureCount;
const NSUInteger kMCPConnectionDefaultOption = CLIENT_COMPRESS | CLIENT_REMEMBER_OPTIONS | CLIENT_MULTI_RESULTS;
const char *kMCPConnectionDefaultSocket = MYSQL_UNIX_ADDR;
@@ -58,6 +59,7 @@ static BOOL sTruncateLongFieldInLogs = YES;
- (void)_getServerVersionString;
- (BOOL)_isCurrentHostReachable;
+- (void)_setupKeepalivePingTimer;
@end
@@ -104,7 +106,6 @@ static BOOL sTruncateLongFieldInLogs = YES;
connectionLogin = nil;
connectionSocket = nil;
connectionPassword = nil;
- keepAliveTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(keepAlive:) userInfo:nil repeats:YES] retain];
keepAliveThread = NULL;
lastKeepAliveTime = 0;
pingThread = NULL;
@@ -117,7 +118,8 @@ static BOOL sTruncateLongFieldInLogs = YES;
serverVersionString = nil;
mTimeZone = nil;
isDisconnecting = NO;
- canPerformAutomaticReconnect = YES;
+ automaticReconnectAttempts = 0;
+ pingFailureCount = 0;
// Initialize ivar defaults
connectionTimeout = 10;
@@ -151,6 +153,12 @@ static BOOL sTruncateLongFieldInLogs = YES;
// Obtain pointers
cStringPtr = [self methodForSelector:cStringSEL];
+
+ // Start the keepalive timer
+ if ([NSThread isMainThread])
+ [self _setupKeepalivePingTimer];
+ else
+ [self performSelectorOnMainThread:@selector(_setupKeepalivePingTimer) withObject:nil waitUntilDone:YES];
}
return self;
@@ -309,16 +317,6 @@ static BOOL sTruncateLongFieldInLogs = YES;
currentProxyState = newState;
[connectionProxy setConnectionStateChangeSelector:nil delegate:nil];
- // If no network is present, loop for a short period waiting for one to become available
- uint64_t elapsedTime_t, networkWaitStartTime_t = mach_absolute_time();
- Nanoseconds elapsedTime;
- while (![self _isCurrentHostReachable]) {
- elapsedTime_t = mach_absolute_time() - networkWaitStartTime_t;
- elapsedTime = AbsoluteToNanoseconds(*(AbsoluteTime *)&(elapsedTime_t));
- if (((double)UnsignedWideToUInt64(elapsedTime)) * 1e-9 > 5) break;
- usleep(250000);
- }
-
// Trigger a reconnect
if (!isDisconnecting) [NSThread detachNewThreadSelector:@selector(reconnect) toTarget:self withObject:nil];
@@ -387,7 +385,7 @@ static BOOL sTruncateLongFieldInLogs = YES;
// Connect
theRet = mysql_real_connect(mConnection, theHost, theLogin, thePass, NULL, connectionPort, theSocket, mConnectionFlags);
thePass = NULL;
-
+
if (theRet != mConnection) {
[self setLastErrorMessage:nil];
@@ -398,7 +396,9 @@ static BOOL sTruncateLongFieldInLogs = YES;
mConnected = YES;
connectionStartTime = mach_absolute_time();
- canPerformAutomaticReconnect = YES;
+ lastKeepAliveTime = 0;
+ automaticReconnectAttempts = 0;
+ pingFailureCount = 0;
mEncoding = [MCPConnection encodingForMySQLEncoding:mysql_character_set_name(mConnection)];
[self setLastErrorMessage:nil];
connectionThreadId = mConnection->thread_id;
@@ -493,6 +493,16 @@ static BOOL sTruncateLongFieldInLogs = YES;
mConnected = NO;
isDisconnecting = NO;
+ // If no network is present, loop for a short period waiting for one to become available
+ uint64_t elapsedTime_t, networkWaitStartTime_t = mach_absolute_time();
+ Nanoseconds elapsedTime;
+ while (![self _isCurrentHostReachable]) {
+ elapsedTime_t = mach_absolute_time() - networkWaitStartTime_t;
+ elapsedTime = AbsoluteToNanoseconds(*(AbsoluteTime *)&(elapsedTime_t));
+ if (((double)UnsignedWideToUInt64(elapsedTime)) * 1e-9 > 5) break;
+ usleep(250000);
+ }
+
// If there is a proxy, ensure it's disconnected and attempt to reconnect it in blocking fashion
if (connectionProxy) {
[connectionProxy setConnectionStateChangeSelector:nil delegate:nil];
@@ -555,16 +565,6 @@ static BOOL sTruncateLongFieldInLogs = YES;
if (mConnection != NULL) {
- // If no network is present, loop for a short period waiting for one to become available
- uint64_t elapsedTime_t, networkWaitStartTime_t = mach_absolute_time();
- Nanoseconds elapsedTime;
- while (![self _isCurrentHostReachable]) {
- elapsedTime_t = mach_absolute_time() - networkWaitStartTime_t;
- elapsedTime = AbsoluteToNanoseconds(*(AbsoluteTime *)&(elapsedTime_t));
- if (((double)UnsignedWideToUInt64(elapsedTime)) * 1e-9 > 5) break;
- usleep(250000);
- }
-
// Attempt to reestablish the connection
[self connect];
}
@@ -635,7 +635,8 @@ static BOOL sTruncateLongFieldInLogs = YES;
// If the connection doesn't appear to be responding, and we can still attempt an automatic
// reconnect (only once each connection - eg an automatic reconnect failure prevents loops,
// but an automatic reconnect success resets the flag for another attempt in future)
- if (!connectionVerified && canPerformAutomaticReconnect) {
+ if (!connectionVerified && automaticReconnectAttempts < 1) {
+ automaticReconnectAttempts++;
// Note that a return of "NO" here has already asked the user, so if reconnect fails,
// return failure.
@@ -785,6 +786,15 @@ void pingConnectionTask(void *ptr)
return;
}
+ // If the maximum number of ping failures has been reached, trigger a reconnect
+ if (pingFailureCount >= 3) {
+ NSAutoreleasePool *connectionPool = [[NSAutoreleasePool alloc] init];
+ [self unlockConnection];
+ [self reconnect];
+ [connectionPool drain];
+ return;
+ }
+
// Use a ping timeout between zero and thirty seconds
NSInteger pingTimeout = 30;
if (connectionTimeout > 0 && connectionTimeout < pingTimeout) pingTimeout = connectionTimeout;
@@ -811,7 +821,11 @@ void pingConnectionTask(void *ptr)
*/
void performThreadedKeepAlive(void *ptr)
{
- mysql_ping((MYSQL *)ptr);
+ if (mysql_ping((MYSQL *)ptr)) {
+ pingFailureCount++;
+ } else {
+ pingFailureCount = 0;
+ }
}
/**
@@ -2926,4 +2940,13 @@ void performThreadedKeepAlive(void *ptr)
return YES;
}
+/**
+ * Set up the keepalive timer; this should be called on the main
+ * thread, to ensure the timer isn't descheduled when child threads
+ * terminate.
+ */
+- (void)_setupKeepalivePingTimer
+{
+ keepAliveTimer = [[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(keepAlive:) userInfo:nil repeats:YES] retain];
+}
@end