Add locking to mbtiles commits -- sqlite3 doesn't allow concurrent transactions.
This commit is contained in:
@@ -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
32
test/commit.test.js
Normal 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();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user