mirror of
https://github.com/mariadb-corporation/mariadb-connector-nodejs.git
synced 2025-08-17 19:26:07 +00:00
permit using pipelining option at query level
pipelining documentation
This commit is contained in:
@ -25,7 +25,7 @@ connection.query("INSERT INTO myTable VALUES (1)");
|
||||
connection.query("INSERT INTO myTable VALUES (2)");
|
||||
```
|
||||
queries are not send one by one, waiting for result before sending next one.
|
||||
queries are send one after another, avoiding a lot of network latency (particulary when database isn't on same host).
|
||||
queries are send one after another, avoiding a lot of network latency ([detail information](/documentation/pipelining.md)).
|
||||
|
||||
Extended documentation of API : [Complete documentation](/documentation/readme.md)
|
||||
|
||||
|
BIN
documentation/misc/pipelining.png
Normal file
BIN
documentation/misc/pipelining.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 99 KiB |
45
documentation/pipelining.md
Normal file
45
documentation/pipelining.md
Normal file
@ -0,0 +1,45 @@
|
||||
## Option "pipelining"
|
||||
|
||||
With traditional synchronous drivers, queries are send one by one, waiting for result before sending next one.
|
||||
Communication with server following a request–response messaging pattern.
|
||||
|
||||
That is not very effective when having to request many query at the same time.
|
||||
Node.js is efficient because of asynchrone concept, let's follow this rules.
|
||||
|
||||
Pipelining is "optimistic send": Connector will send queries one after another, preserving FIFO order.
|
||||
This is particularly efficient when client is distant from server.
|
||||
|
||||
#### Example :
|
||||
create a basket with 3 items
|
||||
|
||||
```javascript
|
||||
connection.beginTransaction();
|
||||
connection.query("INSERT INTO BASKET(customerId) values (?)", [1], (err, res) => {
|
||||
//must handle error if any
|
||||
const basketId = res.insertId;
|
||||
try {
|
||||
connection.query("INSERT INTO BASKET_ITEM(basketId, itemId) values (?, ?)", [basketId, 100]);
|
||||
connection.query("INSERT INTO BASKET_ITEM(basketId, itemId) values (?, ?)", [basketId, 101]);
|
||||
connection.query("INSERT INTO BASKET_ITEM(basketId, itemId) values (?, ?)", [basketId, 102], (err) => {
|
||||
//must handle error if any
|
||||
connection.commit();
|
||||
});
|
||||
} catch (err) {
|
||||
connection.rollback();
|
||||
//handle error
|
||||
}
|
||||
});
|
||||
```
|
||||
#### Network exchanges :
|
||||
<p align="center">
|
||||
<img src="./misc/pipelining.png">
|
||||
</p>
|
||||
|
||||
|
||||
Using standard client-server protocol (aka "ping-pong"), driver communicate with database following a request–response messaging pattern.
|
||||
When sending a command, connector won't send any until response is available from input socket.
|
||||
|
||||
Using option "pipelining", commands are send by bulk, saving network latency.
|
||||
The Inconvenient is that if an error occur on first/second command, following command are already send to database.
|
||||
In that sense that pipelining is "optimistic".
|
||||
|
@ -103,7 +103,7 @@ default: false
|
||||
* `timezone`: string. force using indicated timezone, not current node.js timezone. possible value are 'Z' (fot UTC), 'local' or '±HH:MM' format
|
||||
* `nestTables`: boolean/string. resultset are presented by table to avoid results with colliding fields. default: false
|
||||
* `rowsAsArray`: boolean. default rows are defined as a JSON object. when active row is an array. default false
|
||||
* `pipelining`: boolean. will send query one by one, but without waiting the results of previous entry. default true
|
||||
* `pipelining`: boolean. will send query one by one, but without waiting the results of previous entry ([detail information](/documentation/pipelining.md)). default true
|
||||
|
||||
## Query
|
||||
`connection.query(sql[, values][,callback])`
|
||||
|
@ -153,7 +153,6 @@ class Handshake extends Command {
|
||||
|
||||
if (this.onResult) this.onResult(null);
|
||||
this.connEvents.emit("connect");
|
||||
this.emit("send_end");
|
||||
this.emit("end");
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class Connection {
|
||||
|
||||
this._out = new PacketOutputStream(this.opts, this.info);
|
||||
this._addCommand = this._addCommandEnable;
|
||||
this._addCommand(new Handshake(this));
|
||||
this._addCommand(new Handshake(this), false);
|
||||
|
||||
this.timeoutRef = null;
|
||||
this._closing = null;
|
||||
@ -148,10 +148,11 @@ class Connection {
|
||||
*/
|
||||
query(sql, values, cb) {
|
||||
let _options, _sql, _values, _cb;
|
||||
|
||||
let _pipelining = this.opts.pipelining;
|
||||
if (typeof sql === "object") {
|
||||
_options = sql;
|
||||
_sql = _options.sql;
|
||||
if (_options.pipelining !== undefined) _pipelining = _options.pipelining;
|
||||
} else {
|
||||
_sql = sql;
|
||||
}
|
||||
@ -164,12 +165,12 @@ class Connection {
|
||||
}
|
||||
|
||||
const cmd = new Query(this._events, _options, _sql, _values, _cb);
|
||||
return this._addCommand(cmd);
|
||||
return this._addCommand(cmd, _pipelining);
|
||||
}
|
||||
|
||||
ping(options, callback) {
|
||||
const _cb = typeof options === "function" ? options : callback;
|
||||
return this._addCommand(new Ping(this._events, _cb));
|
||||
return this._addCommand(new Ping(this._events, _cb), false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,7 +192,8 @@ class Connection {
|
||||
this._clear();
|
||||
if (callback) setImmediate(callback);
|
||||
}.bind(this)
|
||||
)
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@ -417,7 +419,7 @@ class Connection {
|
||||
this.info.status & ServerStatus.STATUS_IN_TRANS
|
||||
) {
|
||||
const cmd = new Query(this._events, _options, sql, null, _cb);
|
||||
return this._addCommand(cmd);
|
||||
return this._addCommand(cmd, false);
|
||||
}
|
||||
|
||||
if (_cb) _cb();
|
||||
@ -438,12 +440,10 @@ class Connection {
|
||||
}
|
||||
}
|
||||
|
||||
_addCommandEnable(cmd) {
|
||||
_addCommandEnable(cmd, pipelining) {
|
||||
let conn = this;
|
||||
|
||||
cmd.once(this.opts.pipelining ? "send_end" : "end", () =>
|
||||
setImmediate(conn._nextSendCmd.bind(conn))
|
||||
);
|
||||
cmd.once(pipelining ? "send_end" : "end", () => setImmediate(conn._nextSendCmd.bind(conn)));
|
||||
|
||||
if (!this._sendCmd && this._sendQueue.isEmpty()) {
|
||||
this._sendCmd = cmd;
|
||||
@ -456,7 +456,7 @@ class Connection {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
_addCommandDisabled(cmd) {
|
||||
_addCommandDisabled(cmd, pipelining) {
|
||||
const err = Utils.createError(
|
||||
"Cannot execute new commands: connection closed",
|
||||
true,
|
||||
|
@ -5,7 +5,7 @@ const assert = require("chai").assert;
|
||||
|
||||
describe("pipelining", () => {
|
||||
let conn1, conn2;
|
||||
const iterations = 10000;
|
||||
const iterations = 5000;
|
||||
|
||||
before(function(done) {
|
||||
conn1 = base.createConnection({ pipelining: false });
|
||||
@ -24,20 +24,24 @@ describe("pipelining", () => {
|
||||
conn2.end();
|
||||
});
|
||||
|
||||
it("10000 insert test speed", function(done) {
|
||||
it("5000 insert test speed", function(done) {
|
||||
this.timeout(60000);
|
||||
conn1.query("CREATE TEMPORARY TABLE pipeline1 (test int)");
|
||||
conn2.query("CREATE TEMPORARY TABLE pipeline2 (test int)", (err, res) => {
|
||||
insertBulk(conn1, "pipeline1", diff => {
|
||||
insertBulk(conn2, "pipeline2", pipelineDiff => {
|
||||
assert.isTrue(
|
||||
diff[0] > pipelineDiff[0] || (diff[0] === pipelineDiff[0] && diff[1] > pipelineDiff[1]),
|
||||
"error - time to insert 1000 : std=" +
|
||||
if (shareConn.hasMinVersion(10, 2, 0)) {
|
||||
//before 10.1, speed is sometime nearly equivalent using pipelining or not
|
||||
//remove speed test then to avoid random error in CIs
|
||||
assert.isTrue(
|
||||
diff[0] > pipelineDiff[0] || (diff[0] === pipelineDiff[0] && diff[1] > pipelineDiff[1]),
|
||||
"error - time to insert 1000 : std=" +
|
||||
Math.floor(diff[0] * 1000 + diff[1] / 1000000) +
|
||||
"ms pipelining=" +
|
||||
Math.floor(pipelineDiff[0] * 1000 + pipelineDiff[1] / 1000000) +
|
||||
"ms"
|
||||
);
|
||||
);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user