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
|
// - @param {Function(err)} callback
|
||||||
MBTiles.prototype._commit = function(callback) {
|
MBTiles.prototype._commit = function(callback) {
|
||||||
var mbtiles = this;
|
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.serialize(function() {
|
||||||
mbtiles._db.run('BEGIN');
|
mbtiles._db.run('BEGIN');
|
||||||
|
|
||||||
var statements = {};
|
var statements = {};
|
||||||
Object.keys(mbtiles._writes).forEach(function(table) {
|
Object.keys(writes).forEach(function(table) {
|
||||||
switch (table) {
|
switch (table) {
|
||||||
case 'map':
|
case 'map':
|
||||||
// Insert map table. This has to be so complicate due to a design flaw
|
// 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 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 = ?)))';
|
COALESCE(?, (SELECT grid_id FROM map WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?)))';
|
||||||
statements['map'] = mbtiles._db.prepare(sql);
|
statements['map'] = mbtiles._db.prepare(sql);
|
||||||
for (var id in mbtiles._writes[table]) {
|
for (var id in writes[table]) {
|
||||||
var row = mbtiles._writes[table][id];
|
var row = writes[table][id];
|
||||||
statements['map'].run(
|
statements['map'].run(
|
||||||
row.zoom_level, row.tile_column, row.tile_row,
|
row.zoom_level, row.tile_column, row.tile_row,
|
||||||
row.tile_id, 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 rows = [];
|
||||||
var args = [];
|
var args = [];
|
||||||
var fields = [];
|
var fields = [];
|
||||||
for (var id in mbtiles._writes[table]) {
|
for (var id in writes[table]) {
|
||||||
var record = mbtiles._writes[table][id];
|
var record = writes[table][id];
|
||||||
var row = [];
|
var row = [];
|
||||||
for (var field in record) {
|
for (var field in record) {
|
||||||
row.push(record[field]);
|
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();
|
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