Add locking to mbtiles commits -- sqlite3 doesn't allow concurrent transactions.

This commit is contained in:
Young Hahn
2014-09-30 11:52:04 -04:00
parent 8a7c3da9f3
commit a375ae1df0
2 changed files with 54 additions and 6 deletions

View File

@@ -368,11 +368,23 @@ MBTiles.prototype.write = function(table, id, row, callback) {
// - @param {Function(err)} callback
MBTiles.prototype._commit = function(callback) {
var mbtiles = this;
// If no pending commits our work's done.
if (!mbtiles._pending) return callback();
// If already committing wait in line.
if (mbtiles._committing) return mbtiles.once('commit', function() {
mbtiles._commit(callback);
});
var writes = mbtiles._writes;
mbtiles._clearCaches();
mbtiles._committing = true;
mbtiles._db.serialize(function() {
mbtiles._db.run('BEGIN');
var statements = {};
Object.keys(mbtiles._writes).forEach(function(table) {
Object.keys(writes).forEach(function(table) {
switch (table) {
case 'map':
// Insert map table. This has to be so complicate due to a design flaw
@@ -384,8 +396,8 @@ MBTiles.prototype._commit = function(callback) {
COALESCE(?, (SELECT tile_id FROM map WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?)),\
COALESCE(?, (SELECT grid_id FROM map WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?)))';
statements['map'] = mbtiles._db.prepare(sql);
for (var id in mbtiles._writes[table]) {
var row = mbtiles._writes[table][id];
for (var id in writes[table]) {
var row = writes[table][id];
statements['map'].run(
row.zoom_level, row.tile_column, row.tile_row,
row.tile_id, row.zoom_level, row.tile_column, row.tile_row,
@@ -397,8 +409,8 @@ MBTiles.prototype._commit = function(callback) {
var rows = [];
var args = [];
var fields = [];
for (var id in mbtiles._writes[table]) {
var record = mbtiles._writes[table][id];
for (var id in writes[table]) {
var record = writes[table][id];
var row = [];
for (var field in record) {
row.push(record[field]);
@@ -417,7 +429,11 @@ MBTiles.prototype._commit = function(callback) {
}
});
mbtiles._db.run('COMMIT', callback);
mbtiles._db.run('COMMIT', function(err) {
mbtiles._committing = false;
mbtiles.emit('commit');
callback(err);
});
mbtiles._clearCaches();
});
};

32
test/commit.test.js Normal file
View File

@@ -0,0 +1,32 @@
require('sqlite3').verbose();
var fs = require('fs');
var assert = require('assert');
var MBTiles = require('..');
var fixtureDir = __dirname + '/fixtures/output';
var image = fs.readFileSync(__dirname + '/fixtures/images/plain_1_0_0_0.png');
describe('write', function() {
before(function(done) {
// Recreate output directory to remove previous tests.
try { fs.unlinkSync(fixtureDir + '/commit_1.mbtiles'); } catch(err) {}
try { fs.mkdirSync(fixtureDir, 0755); } catch(err) {}
done();
});
it('test mbtiles commit lock', function(done) {
var remaining = 10;
new MBTiles('mbtiles://' + fixtureDir + '/commit_1.mbtiles?batch=1', function(err, mbtiles) {
assert.ifError(err);
mbtiles.startWriting(function(err) {
assert.ifError(err);
for (var i = 0; i < remaining; i++) mbtiles.putTile(0,0,0,image,putcb);
assert.equal(mbtiles._committing, true);
assert.equal(mbtiles._events.commit.length, 19);
});
});
function putcb(err) {
assert.ifError(err);
if (!--remaining) done();
}
});
});