forked from couchbaselabs/TouchDB-iOS
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Overhauled checkpointing for pull replications
Added TDSequenceMap to keep track of the latest sequence ID we can checkpoint. (Should be used for push replications too, but I haven't done that work yet.)
- Loading branch information
Showing
5 changed files
with
198 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// | ||
// TDSequenceMap.h | ||
// TouchDB | ||
// | ||
// Created by Jens Alfke on 2/21/12. | ||
// Copyright (c) 2012 Couchbase, Inc. All rights reserved. | ||
// | ||
|
||
#import "TDRevision.h" | ||
|
||
|
||
/** A data structure representing a type of array that allows object values to be added to the end, and removed in arbitrary order; it's used by the replicator to keep track of which revisions have been transferred and what sequences to checkpoint. */ | ||
@interface TDSequenceMap : NSObject | ||
{ | ||
NSMutableIndexSet* _sequences; // Sequence numbers currently in the map | ||
NSUInteger _lastSequence; // last generated sequence | ||
NSMutableArray* _values; // values of remaining sequences | ||
NSUInteger _firstValueSequence; // sequence # of first item in _values | ||
} | ||
|
||
- (id) init; | ||
|
||
/** Adds a value to the map, assigning it a sequence number and returning it. | ||
Sequence numbers start at 1 and increment from there. */ | ||
- (SequenceNumber) addValue: (id)value; | ||
|
||
/** Removes a sequence and its associated value. */ | ||
- (void) removeSequence: (SequenceNumber)sequence; | ||
|
||
@property (readonly) BOOL isEmpty; | ||
|
||
/** Returns the maximum consecutively-removed sequence number. | ||
This is one less than the minimum remaining sequence number. */ | ||
- (SequenceNumber) checkpointedSequence; | ||
|
||
/** Returns the value associated with the checkpointedSequence. */ | ||
- (id) checkpointedValue; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// | ||
// TDSequenceMap.m | ||
// TouchDB | ||
// | ||
// Created by Jens Alfke on 2/21/12. | ||
// Copyright (c) 2012 Couchbase, Inc. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file | ||
// except in compliance with the License. You may obtain a copy of the License at | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// Unless required by applicable law or agreed to in writing, software distributed under the | ||
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, | ||
// either express or implied. See the License for the specific language governing permissions | ||
// and limitations under the License. | ||
|
||
#import "TDSequenceMap.h" | ||
|
||
|
||
@implementation TDSequenceMap | ||
|
||
|
||
- (id)init | ||
{ | ||
self = [super init]; | ||
if (self) { | ||
_sequences = [[NSMutableIndexSet alloc] init]; | ||
_values = [[NSMutableArray alloc] initWithCapacity: 100]; | ||
_firstValueSequence = 1; | ||
} | ||
return self; | ||
} | ||
|
||
|
||
- (void)dealloc | ||
{ | ||
[_sequences release]; | ||
[_values release]; | ||
[super dealloc]; | ||
} | ||
|
||
|
||
- (SequenceNumber) addValue: (id)value { | ||
[_sequences addIndex: ++_lastSequence]; | ||
[_values addObject: value]; | ||
return _lastSequence; | ||
} | ||
|
||
|
||
- (void) removeSequence: (SequenceNumber)sequence { | ||
Assert(sequence > 0 && sequence <= (SequenceNumber)_lastSequence, | ||
@"Invalid sequence %lld (latest is %u)", sequence, _lastSequence); | ||
[_sequences removeIndex: (NSUInteger) sequence]; | ||
} | ||
|
||
|
||
- (BOOL) isEmpty { | ||
return _sequences.firstIndex == NSNotFound; | ||
} | ||
|
||
|
||
- (SequenceNumber) checkpointedSequence { | ||
NSUInteger sequence = _sequences.firstIndex; | ||
sequence = (sequence == NSNotFound) ? _lastSequence : sequence-1; | ||
|
||
if (sequence > _firstValueSequence) { | ||
// Garbage-collect inaccessible values: | ||
NSUInteger numToRemove = sequence - _firstValueSequence; | ||
[_values removeObjectsInRange: NSMakeRange(0, numToRemove)]; | ||
_firstValueSequence += numToRemove; | ||
} | ||
return sequence; | ||
} | ||
|
||
|
||
- (id) checkpointedValue { | ||
NSInteger index = (NSInteger)([self checkpointedSequence] - _firstValueSequence); | ||
return (index >= 0) ? [_values objectAtIndex: index] : nil; | ||
} | ||
|
||
|
||
@end | ||
|
||
|
||
|
||
TestCase(TDSequenceMap) { | ||
TDSequenceMap* map = [[[TDSequenceMap alloc] init] autorelease]; | ||
CAssertEq(map.checkpointedSequence, 0); | ||
CAssertEqual(map.checkpointedValue, nil); | ||
CAssert(map.isEmpty); | ||
|
||
CAssertEq([map addValue: @"one"], 1); | ||
CAssertEq(map.checkpointedSequence, 0); | ||
CAssertEqual(map.checkpointedValue, nil); | ||
CAssert(!map.isEmpty); | ||
|
||
CAssertEq([map addValue: @"two"], 2); | ||
CAssertEq(map.checkpointedSequence, 0); | ||
CAssertEqual(map.checkpointedValue, nil); | ||
|
||
CAssertEq([map addValue: @"three"], 3); | ||
CAssertEq(map.checkpointedSequence, 0); | ||
CAssertEqual(map.checkpointedValue, nil); | ||
|
||
[map removeSequence: 2]; | ||
CAssertEq(map.checkpointedSequence, 0); | ||
CAssertEqual(map.checkpointedValue, nil); | ||
|
||
[map removeSequence: 1]; | ||
CAssertEq(map.checkpointedSequence, 2); | ||
CAssertEqual(map.checkpointedValue, @"two"); | ||
|
||
CAssertEq([map addValue: @"four"], 4); | ||
CAssertEq(map.checkpointedSequence, 2); | ||
CAssertEqual(map.checkpointedValue, @"two"); | ||
|
||
[map removeSequence: 3]; | ||
CAssertEq(map.checkpointedSequence, 3); | ||
CAssertEqual(map.checkpointedValue, @"three"); | ||
|
||
[map removeSequence: 4]; | ||
CAssertEq(map.checkpointedSequence, 4); | ||
CAssertEqual(map.checkpointedValue, @"four"); | ||
CAssert(map.isEmpty); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters