I'm working on a simple service using Node.js. It receives uploaded files, stores them on the disk and records some metadata on an Oracle table. I'm using the db-oracle
package together with connection pooling, following this article: http://nodejsdb.org/2011/05/connection-pooling-node-db-with-generic-pool/
However, I've noticed that the data I insert is only sent to the Oracle database after the connection pool closes the idle connection, by calling its disconnect()
method.
Is there a way to flush the data before sending the 'OK' signal to my client? The way it is working now, a crash on my webservice or on Oracle itself can cause loss of data, and the client of my service would not know about it. I actually tested this by killing my app process after some uploads, and the data was indeed lost.
Here's a simplified version of the code:
var express = require('express');
var app = module.exports = express.createServer();
app.post('/upload', handleUpload);
app.listen(4001, function(){
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
});
function handleUpload(req, res) {
res.contentType('application/xml');
var buf = '';
req.on('data', function(chunk) { buf += chunk; });
req.on('end', function() {
saveUpload(req, res, buf);
});
}
function saveUpload(req, res, buf) {
if (buf.length == 0)
return sendError(res, 'No data supplied', 422);
var payload = new Buffer(buf, 'base64');
files.save(payload, function(err, savedFile) {
if (err)
return sendError(res, 'Error while saving', 500);
var obj = { ip: req.connection.remoteAddress, location: savedFile.path,
created_at: new Date(), updated_at: new Date() };
var fields = ['IP', 'LOCATION', 'CREATED_AT', 'UPDATED_AT'];
var values = fields.map(function(v) { return obj[v.toLowerCase()] });
pool.acquire(function(err, conn) {
if (err)
return sendError(res, err, 500);
var q = conn.query().insert('FILES', fields, values);
q.execute(function(err, result) {
pool.release(conn);
if (err)
return sendError(res, err, 500);
if (result.affected < 1)
return sendError(res, 'Error saving the record', 500);
// The next statement sends the final result to the client.
// However, the new record was not yet flushed to the database.
res.end('<ok />');
});
});
});
}
function sendError(res, err, code) {
console.log(err);
res.send('<error>' + err + '</error>', code || 500);
}
As a workaround, I've tried to implement a fake connection pool and release all acquired connections, but now my app is dying with the message: pure virtual method calledAbort trap: 6
Here's the fake connection pooling:
var fakePool = {
acquire: function(callback) {
new oracle.Database(config.database).connect(function(err, server) {
callback(err, this);
});
},
release: function(conn) {
conn.disconnect();
}
};
Just to be clear, I don't care about the fake connection pooler, it was just a dirty workaround. I want to be able to flush the data to Oracle before sending the 'OK' to my client.
Btw I also opened a ticket on their Github: https://github.com/mariano/node-db-oracle/issues/38
You are obviously missing a transaction commit.
node-db does not need to expose a commit API because in most RDBMS (including Oracle), COMMIT is a valid query. Since the package allows the execution of arbitrary queries, commit/rollback are supposed to be done using a simple execute()
The code should be changed as follows:
pool.acquire(function(err, conn) {
if (err)
return sendError(res, err, 500);
var q = conn.query().insert('FILES', fields, values);
q.execute(function(err, result) {
if (err || result.affected < 1 ) {
pool.release(conn);
return sendError(res, err, 500);
}
conn.query().execute("commit", function(err,result) {
if (err) {
pool.release(conn);
return sendError(res, err, 500);
}
res.end('<ok />');
pool.release(conn);
});
});
});
This is not an exact answer to your question, but have a look at node-oracle
package.
It lacks connection pooling, but its commit
/ rollback
functionality can at least be controlled from code. And you can always mix it with a generic pool solution such as node-pool
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With