Permalink
Browse files

Merge remote-tracking branch 'origin/master' into syncpoint

  • Loading branch information...
2 parents 2244603 + 92a6edb commit 40c3f16d04c4a4acf4120f23582a710bf2e888b5 @snej snej committed Jun 6, 2012
View
1 Source/ChangeTracker/TDConnectionChangeTracker.h
@@ -17,6 +17,7 @@
NSURLConnection* _connection;
NSMutableData* _inputBuffer;
unsigned _retryCount;
+ CFAbsoluteTime _startTime;
}
@end
View
28 Source/ChangeTracker/TDConnectionChangeTracker.m
@@ -40,6 +40,7 @@ - (BOOL) start {
}];
_connection = [[NSURLConnection connectionWithRequest: request delegate: self] retain];
+ _startTime = CFAbsoluteTimeGetCurrent();
LogTo(ChangeTracker, @"%@: Started... <%@>", self, request.URL);
return YES;
}
@@ -105,16 +106,33 @@ - (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// Now parse the entire response as a JSON document:
NSData* input = [_inputBuffer retain];
LogTo(ChangeTracker, @"%@: Got entire body, %u bytes", self, (unsigned)input.length);
+ BOOL restart = NO;
NSInteger numChanges = [self receivedPollResponse: input];
- if (numChanges < 0)
- [self setUpstreamError: @"Unparseable server response"];
+ if (numChanges < 0) {
+ // Oops, unparseable response:
+ if (_mode == kLongPoll && [input isEqualToData: [@"{\"results\":[\n"
+ dataUsingEncoding: NSUTF8StringEncoding]]) {
+ // Looks like the connection got closed by a proxy (like AWS' load balancer) before
+ // the server had an actual change to send.
+ NSTimeInterval elapsed = CFAbsoluteTimeGetCurrent() - _startTime;
+ Warn(@"%@: Longpoll connection closed (by proxy?) after %.1f sec", self, elapsed);
+ if (elapsed >= 30.0 && elapsed < _heartbeat) {
+ self.heartbeat = elapsed * 0.75;
+ restart = YES;
+ }
+ }
+ if (!restart)
+ [self setUpstreamError: @"Unparseable server response"];
+ } else {
+ // Poll again if there was no error, and either we're in longpoll mode or it looks like we
+ // ran out of changes due to a _limit rather than because we hit the end.
+ restart = (numChanges > 0 && (_mode == kLongPoll || numChanges == (NSInteger)_limit));
+ }
[input release];
[self clearConnection];
- // Poll again if there was no error, and either we're in longpoll mode or it looks like we
- // ran out of changes due to a _limit rather than because we hit the end.
- if (numChanges > 0 && (_mode == kLongPoll || numChanges == (NSInteger)_limit))
+ if (restart)
[self start]; // Next poll...
else
[self stopped];
View
2 Source/TDPuller.m
@@ -114,7 +114,7 @@ - (void) startChangeTracker {
_changeTracker.filterParameters = _filterParameters;
unsigned heartbeat = $castIf(NSNumber, [_options objectForKey: @"heartbeat"]).unsignedIntValue;
if (heartbeat >= 15000)
- _changeTracker.heartbeat = heartbeat;
+ _changeTracker.heartbeat = heartbeat / 1000.0;
NSMutableDictionary* headers = $mdict({@"User-Agent", [TDRemoteRequest userAgentHeader]});
[headers addEntriesFromDictionary: _requestHeaders];
View
22 Source/TDView.m
@@ -481,13 +481,23 @@ - (NSArray*) queryWithOptions: (const TDQueryOptions*)options
} else {
// Regular query:
NSString* docID = [r stringForColumnIndex: 2];
- NSDictionary* docContents = nil;
+ id docContents = nil;
if (options->includeDocs) {
- docContents = [_db documentPropertiesFromJSON: [r dataNoCopyForColumnIndex: 4]
- docID: docID
- revID: [r stringForColumnIndex: 3]
- sequence: [r longLongIntForColumnIndex:5]
- options: options->content];
+ NSString* linkedID = [value objectForKey: @"_id"];
+ if (linkedID) {
+ // Linked document: http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents
+ NSString* linkedRev = [value objectForKey: @"_rev"]; // usually nil
+ TDRevision* linked = [_db getDocumentWithID: linkedID
+ revisionID: linkedRev
+ options: options->content];
+ docContents = linked ? linked.properties : $null;
+ } else {
+ docContents = [_db documentPropertiesFromJSON: [r dataNoCopyForColumnIndex: 4]
+ docID: docID
+ revID: [r stringForColumnIndex: 3]
+ sequence: [r longLongIntForColumnIndex:5]
+ options: options->content];
+ }
}
[rows addObject: $dict({@"id", docID},
{@"key", key},
View
47 Source/TDView_Tests.m
@@ -604,4 +604,51 @@
}
+TestCase(TDView_LinkedDocs) {
+ RequireTestCase(TDView_Query);
+ TDDatabase *db = createDB();
+ NSArray* revs = putDocs(db);
+
+ NSDictionary* docs[5];
+ int i = 0;
+ for (TDRevision* rev in revs) {
+ docs[i++] = [db getDocumentWithID: rev.docID revisionID: rev.revID options: 0].properties;
+ }
+
+ TDView* view = [db viewNamed: @"linkview"];
+ [view setMapBlock: ^(NSDictionary* doc, TDMapEmitBlock emit) {
+ NSString* key = [doc objectForKey: @"key"];
+ NSDictionary* value = nil;
+ int linkedID = [[doc objectForKey: @"_id"] intValue] - 11111;
+ if (linkedID > 0)
+ value = $dict({@"_id", $sprintf(@"%d", linkedID)});
+ emit(key, value);
+ } reduceBlock: NULL version: @"1"];
+
+ CAssertEq([view updateIndex], kTDStatusOK);
+
+ // Query all rows:
+ TDQueryOptions options = kDefaultTDQueryOptions;
+ options.includeDocs = YES;
+ TDStatus status;
+ NSArray* rows = [view queryWithOptions: &options status: &status];
+ NSArray* expectedRows = $array($dict({@"id", @"55555"}, {@"key", @"five"},
+ {@"value", $dict({@"_id", @"44444"})},
+ {@"doc", docs[1]}),
+ $dict({@"id", @"44444"}, {@"key", @"four"},
+ {@"value", $dict({@"_id", @"33333"})},
+ {@"doc", docs[3]}),
+ $dict({@"id", @"11111"}, {@"key", @"one"},
+ {@"doc", docs[2]}),
+ $dict({@"id", @"33333"}, {@"key", @"three"},
+ {@"value", $dict({@"_id", @"22222"})},
+ {@"doc", docs[0]}),
+ $dict({@"id", @"22222"}, {@"key", @"two"},
+ {@"value", $dict({@"_id", @"11111"})},
+ {@"doc", docs[2]}));
+ CAssertEqual(rows, expectedRows);
+
+}
+
+
#endif

0 comments on commit 40c3f16

Please sign in to comment.