Skip to content

Commit

Permalink
Adding ability to run VACUUM command (synchronously or asynchronously)
Browse files Browse the repository at this point in the history
  • Loading branch information
robbiehanson committed Aug 26, 2014
1 parent 9ef6979 commit 3bd290a
Show file tree
Hide file tree
Showing 3 changed files with 563 additions and 25 deletions.
179 changes: 155 additions & 24 deletions Testing/Xcode-mobile/YapDatabase/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,103 @@ - (NSString *)randomLetters:(NSUInteger)length
return result;
}

static const NSUInteger COUNT = 2500;
static const NSUInteger STR_LENGTH = 2000;

- (void)asyncFillDatabase:(YapDatabaseConnection *)connection after:(const NSTimeInterval)delayInSeconds
{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

for (int i = 0; i < COUNT; i++)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

[transaction setObject:[self randomLetters:STR_LENGTH] forKey:key inCollection:nil];
}
}];
});
}

- (void)asyncFillOddIndexes:(YapDatabaseConnection *)connection after:(const NSTimeInterval)delayInSeconds
{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

for (int i = 1; i < COUNT; i += 2)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

[transaction setObject:[self randomLetters:STR_LENGTH] forKey:key inCollection:nil];
}
}];
});
}

- (void)asyncFillEvenIndexes:(YapDatabaseConnection *)connection after:(const NSTimeInterval)delayInSeconds
{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

for (int i = 0; i < COUNT; i += 2)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

[transaction setObject:[self randomLetters:STR_LENGTH] forKey:key inCollection:nil];
}
}];
});
}

- (void)asyncDeleteOddIndexes:(YapDatabaseConnection *)connection after:(const NSTimeInterval)delayInSeconds
{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

for (int i = 1; i < COUNT; i+=2)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

[transaction removeObjectForKey:key inCollection:nil];
}
}];
});
}

- (void)asyncDeleteEvenIndexes:(YapDatabaseConnection *)connection after:(const NSTimeInterval)delayInSeconds
{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

[connection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

for (int i = 0; i < COUNT; i+=2)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

[transaction removeObjectForKey:key inCollection:nil];
}
}];
});
}

- (void)asyncVacuumAfter:(const NSTimeInterval)delayInSeconds
{
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

[[database newConnection] asyncVacuumWithCompletionBlock:NULL];
});
}

- (void)debug
{
NSLog(@"Starting debug...");
Expand All @@ -102,43 +199,77 @@ - (void)debug
[[NSFileManager defaultManager] removeItemAtPath:databasePath error:nil];

database = [[YapDatabase alloc] initWithPath:databasePath];
databaseConnection = [database newConnection];

// Fill up the database with stuff

[[database newConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

for (int i = 0; i < 1500; i++)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

[transaction setObject:[self randomLetters:100] forKey:key inCollection:nil];
}
}];
dispatch_time_t when;

NSTimeInterval after = 0.5;

[self asyncFillDatabase:databaseConnection after:after]; after += 1.5;

// Delete a bunch of stuff (to make more pages in the WAL)
[self asyncDeleteEvenIndexes:databaseConnection after:after]; after += 1.5;
[self asyncFillEvenIndexes:databaseConnection after:after]; after += 1.5;

[[database newConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[self asyncDeleteOddIndexes:databaseConnection after:after]; after += 1.5;
[self asyncFillOddIndexes:databaseConnection after:after]; after += 1.5;

[self asyncFillEvenIndexes:databaseConnection after:after]; after += 1.5;
[self asyncFillOddIndexes:databaseConnection after:after]; after += 1.5;

[self asyncDeleteEvenIndexes:databaseConnection after:after]; after += 1.5;
[self asyncFillEvenIndexes:databaseConnection after:after]; after += 1.5;

[self asyncDeleteOddIndexes:databaseConnection after:after]; after += 1.5;
[self asyncFillOddIndexes:databaseConnection after:after]; after += 1.5;

[self asyncFillEvenIndexes:databaseConnection after:after]; after += 1.5;
[self asyncFillOddIndexes:databaseConnection after:after]; after += 1.5;

when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(after * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

for (int i = 0; i < 1000; i+=2)
{
NSString *key = [NSString stringWithFormat:@"%d", i];
[databaseConnection asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) {

[transaction removeObjectForKey:key inCollection:nil];
}
}];
NSLog(@"Preparing to sleep read transaction...");
[NSThread sleepForTimeInterval:0.25];

NSLog(@"Fetching items...");

for (int i = 0; i < COUNT; i++)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

(void)[transaction objectForKey:key inCollection:nil];
}

NSLog(@"Preparing to sleep read transaction...");
[NSThread sleepForTimeInterval:2.0];

NSLog(@"Fetching more items...");

for (int i = 0; i < COUNT; i++)
{
NSString *key = [NSString stringWithFormat:@"%d", i];

(void)[transaction objectForKey:key inCollection:nil];
}

NSLog(@"Read transaction complete");
}];
});

NSLog(@"database WAL should be fairly big now...");
[self asyncVacuumAfter:after];

// This next change should reset the WAL file size (to something much smaller)
after += 4.0;

dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(after * NSEC_PER_SEC));
dispatch_after(when, dispatch_get_main_queue(), ^{

NSLog(@"This change should reset the WAL file size to something much smaller...");

[[database newConnection] readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[databaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {

[transaction setObject:@"bar" forKey:@"foo" inCollection:nil];
[transaction setObject:@"quack" forKey:@"quack" inCollection:@"animals"];
}];
});
}
Expand Down
84 changes: 84 additions & 0 deletions YapDatabase/YapDatabaseConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,4 +504,88 @@ __attribute((deprecated("Use method asyncReadWriteWithBlock:completionQueue:comp
@property (atomic, assign, readwrite) YapDatabaseConnectionFlushMemoryFlags autoFlushMemoryFlags;
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Vacuum
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Performs a VACUUM on the sqlite database.
*
* This method operates as a synchronous ReadWrite "transaction".
* That is, it behaves in a similar fashion, and you may treat it as if it is a ReadWrite transaction.
*
* For more infomation on the VACUUM operation, see the sqlite docs:
* http://sqlite.org/lang_vacuum.html
*
* Remember that YapDatabase operates in WAL mode, with "auto_vacuum=FULL" set.
*
* Upgrade Notice:
* The "auto_vacuum=FULL" was not properly set until YapDatabase v2.5.
* And thus if you have an app that was using YapDatabase prior to this version,
* then the existing database file will continue to operate in "auto_vacuum=NONE" mode.
* This means the existing database file won't be properly truncated as you delete information from the db.
* That is, the data will be removed, but the pages will be moved to the freelist,
* and the file itself will remain the same size on disk.
* To correct this problem, you should run the vacuum operation is at least once.
* After it is run, the "auto_vacuum=FULL" mode will be set,
* and the database file size will automatically shrink in the future (as you delete data).
**/
- (void)vacuum;

/**
* Performs a VACUUM on the sqlite database.
*
* This method operates as an asynchronous readWrite "transaction".
* That is, it behaves in a similar fashion, and you may treat it as if it is a ReadWrite transaction.
*
* For more infomation on the VACUUM operation, see the sqlite docs:
* http://sqlite.org/lang_vacuum.html
*
* Remember that YapDatabase operates in WAL mode, with "auto_vacuum=FULL" set.
*
* Upgrade Notice:
* The "auto_vacuum=FULL" was not properly set until YapDatabase v2.5.
* And thus if you have an app that was using YapDatabase prior to this version,
* then the existing database file will continue to operate in "auto_vacuum=NONE" mode.
* This means the existing database file won't be properly truncated as you delete information from the db.
* That is, the data will be removed, but the pages will be moved to the freelist,
* and the file itself will remain the same size on disk.
* To correct this problem, you should run the vacuum operation is at least once.
* After it is run, the "auto_vacuum=FULL" mode will be set,
* and the database file size will automatically shrink in the future (as you delete data).
*
* An optional completion block may be used.
* The completionBlock will be invoked on the main thread (dispatch_get_main_queue()).
**/
- (void)asyncVacuumWithCompletionBlock:(dispatch_block_t)completionBlock;

/**
* Performs a VACUUM on the sqlite database.
*
* This method operates as an asynchronous readWrite "transaction".
* That is, it behaves in a similar fashion, and you may treat it as if it is a ReadWrite transaction.
*
* For more infomation on the VACUUM operation, see the sqlite docs:
* http://sqlite.org/lang_vacuum.html
*
* Remember that YapDatabase operates in WAL mode, with "auto_vacuum=FULL" set.
*
* Upgrade Notice:
* The "auto_vacuum=FULL" was not properly set until YapDatabase v2.5.
* And thus if you have an app that was using YapDatabase prior to this version,
* then the existing database file will continue to operate in "auto_vacuum=NONE" mode.
* This means the existing database file won't be properly truncated as you delete information from the db.
* That is, the data will be removed, but the pages will be moved to the freelist,
* and the file itself will remain the same size on disk.
* To correct this problem, you should run the vacuum operation is at least once.
* After it is run, the "auto_vacuum=FULL" mode will be set,
* and the database file size will automatically shrink in the future (as you delete data).
*
* An optional completion block may be used.
* Additionally the dispatch_queue to invoke the completion block may also be specified.
* If NULL, dispatch_get_main_queue() is automatically used.
**/
- (void)asyncVacuumWithCompletionQueue:(dispatch_queue_t)completionQueue
completionBlock:(dispatch_block_t)completionBlock;

@end
Loading

0 comments on commit 3bd290a

Please sign in to comment.