Skip to content

Commit acea0b9

Browse files
authored
NODEJS-691 Fix generated timestamp on retry (#438)
1 parent c0a6c5f commit acea0b9

File tree

2 files changed

+144
-2
lines changed

2 files changed

+144
-2
lines changed

lib/requests.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ class ExecuteRequest extends Request {
121121
this.params = params;
122122
this.meta = meta;
123123
this.options = execOptions || ExecutionOptions.empty();
124+
this.timestamp = this.options.getOrGenerateTimestamp();
124125
this.consistency = this.options.getConsistency() || types.consistencies.one;
125126
// Only QUERY request parameters are encoded as named parameters
126127
// EXECUTE request parameters are always encoded as positional parameters
@@ -174,7 +175,7 @@ class ExecuteRequest extends Request {
174175
// [<serial_consistency>][<timestamp>][keyspace][continuous_paging_options]
175176
let flags = 0;
176177

177-
const timestamp = this.options.getOrGenerateTimestamp();
178+
const timestamp = this.timestamp;
178179

179180
if (types.protocolVersion.supportsPaging(encoder.protocolVersion)) {
180181
flags |= (this.params && this.params.length) ? queryFlag.values : 0;
@@ -407,6 +408,7 @@ class BatchRequest extends Request {
407408
super();
408409
this.queries = queries;
409410
this.options = execOptions;
411+
this.timestamp = this.options.getOrGenerateTimestamp();
410412
this.hints = execOptions.getHints() || utils.emptyArray;
411413
this.type = batchType.logged;
412414

@@ -463,7 +465,7 @@ class BatchRequest extends Request {
463465
if (types.protocolVersion.supportsTimestamp(encoder.protocolVersion)) {
464466
// Batch flags
465467
let flags = this.options.getSerialConsistency() ? batchFlag.withSerialConsistency : 0;
466-
const timestamp = this.options.getOrGenerateTimestamp();
468+
const timestamp = this.timestamp;
467469
flags |= timestamp !== null && timestamp !== undefined ? batchFlag.withDefaultTimestamp : 0;
468470

469471
flags |= this.options.getKeyspace() && types.protocolVersion.supportsKeyspaceInRequest(encoder.protocolVersion)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright DataStax, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
'use strict';
17+
18+
const assert = require('assert');
19+
const requests = require('../../lib/requests');
20+
const Encoder = require('../../lib/encoder');
21+
const types = require('../../lib/types');
22+
const utils = require('../../lib/utils');
23+
const DefaultExecutionOptions = require('../../lib/execution-options').DefaultExecutionOptions;
24+
const ExecutionProfile = require('../../lib/execution-profile').ExecutionProfile;
25+
const QueryRequest = requests.QueryRequest;
26+
const ExecuteRequest = requests.ExecuteRequest;
27+
const BatchRequest = requests.BatchRequest;
28+
const defaultOptions = require('../../lib/client-options').defaultOptions;
29+
30+
const encoder = new Encoder(types.protocolVersion.maxSupported, {});
31+
32+
describe('ExecuteRequest', function () {
33+
describe('#write()', function() {
34+
testGenerateTimestamp({ fetchSize: 0 }, getExecuteRequest, getExecuteRequestExpectedBuffer);
35+
});
36+
});
37+
38+
describe('QueryRequest', function () {
39+
describe('#write()', function () {
40+
testGenerateTimestamp({ fetchSize: 0 }, getQueryRequest, getQueryRequestExpectedBuffer);
41+
});
42+
});
43+
44+
describe('BatchRequest', function () {
45+
describe('#write()', function () {
46+
testGenerateTimestamp({ logged: false, consistency: 1 }, getBatchRequest, getBatchRequestExpectedBuffer);
47+
});
48+
});
49+
50+
function testGenerateTimestamp(queryOptions, requestGetter, bufferGetter) {
51+
it('should generate the timestamp once per request', function () {
52+
assert.strictEqual(queryOptions.timestamp, undefined);
53+
const client = getClientFake();
54+
const nbCalls = 4;
55+
for (let timestamp = 0; timestamp < nbCalls; timestamp++) {
56+
const request = requestGetter(client, queryOptions);
57+
for (let i = 0; i < nbCalls; i++) {
58+
assert.deepEqual(request.write(encoder, 0), bufferGetter(timestamp));
59+
}
60+
}
61+
});
62+
}
63+
64+
function getExecuteRequestExpectedBuffer(timestamp) {
65+
return Buffer.concat([
66+
utils.allocBufferFromArray([
67+
types.protocolVersion.maxSupported,
68+
0, 0, 0, 0xA, // flags + stream id + opcode (0xA = execute)
69+
0, 0, 0, 0x16, // length
70+
0, 2, 0x51, 0x31, // id length = 2 + id (Q1)
71+
0, 2, 0x52, 0x31, // result id length = 2 + id (Q1)
72+
0, 1, 0, 0, 0, 0x20, // consistency level + flags (0x20 = timestamp)
73+
]),
74+
longBuffer(timestamp)
75+
]);
76+
}
77+
78+
function getQueryRequestExpectedBuffer(timestamp) {
79+
return Buffer.concat([
80+
utils.allocBufferFromArray([
81+
types.protocolVersion.maxSupported,
82+
0, 0, 0, 0x7, // flags + stream id + opcode (0x7 = query)
83+
0, 0, 0, 0x14, // length
84+
0, 0, 0, 2, 0x51, 0x31, // query, length = 2, 'Q1'
85+
0, 1, 0, 0, 0, 0x20, // consistency level + flags (0x20 = timestamp)
86+
]),
87+
longBuffer(timestamp)
88+
]);
89+
}
90+
91+
function getBatchRequestExpectedBuffer(timestamp) {
92+
return Buffer.concat([
93+
utils.allocBufferFromArray([
94+
types.protocolVersion.maxSupported,
95+
0, 0, 0, 0xD, // flags + stream id + opcode (0xD = batch)
96+
0, 0, 0, 0x23, // length
97+
1, 0, 2, // 1 = unlogged, 2 queries
98+
0, 0, 0, 0, 2, 0x51, 0x31, 0, 0, // simple query, length = 2, 'Q1', 0 values
99+
0, 0, 0, 0, 2, 0x51, 0x32, 0, 0, // simple query, length = 2, 'Q2', 0 values
100+
0, 1, 0, 0, 0, 0x20, // consistency level + flags (0x20 = timestamp)
101+
]),
102+
longBuffer(timestamp)
103+
]);
104+
}
105+
106+
function longBuffer(value) {
107+
value = types.Long.fromNumber(value);
108+
return types.Long.toBuffer(value);
109+
}
110+
111+
function getExecuteRequest(client, options) {
112+
const execOptions = DefaultExecutionOptions.create(options, client);
113+
const meta = { resultId: utils.allocBufferFromString('R1'), columns: [ { } ] };
114+
return new ExecuteRequest('Q1', utils.allocBufferFromString('Q1'), [], execOptions, meta);
115+
}
116+
117+
function getQueryRequest(client, options) {
118+
const execOptions = DefaultExecutionOptions.create(options, client);
119+
return new QueryRequest('Q1', [], execOptions);
120+
}
121+
122+
function getBatchRequest(client, options) {
123+
const execOptions = DefaultExecutionOptions.create(options, client);
124+
return new BatchRequest(
125+
[
126+
{ query: 'Q1', params: [] },
127+
{ query: 'Q2', params: [] }
128+
], execOptions);
129+
}
130+
131+
function getClientFake() {
132+
const clientOptions = defaultOptions();
133+
let timestamp = 0;
134+
clientOptions.policies.timestampGeneration.next = () => timestamp++;
135+
return {
136+
profileManager: { getProfile: x => new ExecutionProfile(x || 'default') },
137+
options: clientOptions,
138+
controlConnection: { protocolVersion: types.protocolVersion.maxSupported }
139+
};
140+
}

0 commit comments

Comments
 (0)