Skip to content
This repository was archived by the owner on Mar 26, 2026. It is now read-only.

Commit 5895404

Browse files
authored
chore: Common pitfalls unit test (#1564)
* clean up server deprecated code * test passes now with changes to test proxy * remove passing test * remove await * fix sync repo settings * grab app profileId from client * remove passing test * changes to timeout * fix deadline error * remove package.json line * fix prettier issues * remove old functions for deadline * Update bulk-mutate-rows.js * Update read-rows.js * Update read-modify-write-row.js * Update read-row.js * add unit test that confirms a common pitfall * fix error syntax * remove describe block * modify code to have server send deadline exceeded error * add modified unit test with hook * have hook handle requests * add test that confirms a pitfall * fix typo * fix prettier * remove unused import
1 parent 2fdf98f commit 5895404

3 files changed

Lines changed: 65 additions & 2 deletions

File tree

test/readrows.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,46 @@ describe('Bigtable/ReadRows', () => {
461461
})();
462462
});
463463

464+
it.skip('pitfall: should not request full table scan during a retry on a transient error', async () => {
465+
const requests = [];
466+
467+
const TRANSIENT_ERROR_SERVICE: ReadRowsServiceParameters = {
468+
chunkSize: CHUNK_SIZE,
469+
valueSize: VALUE_SIZE,
470+
chunksPerResponse: CHUNKS_PER_RESPONSE,
471+
keyFrom: STANDARD_KEY_FROM,
472+
keyTo: STANDARD_KEY_TO,
473+
deadlineExceededError: true,
474+
hook: request => {
475+
requests.push(request);
476+
},
477+
debugLog,
478+
};
479+
480+
async function readRowsWithDeadline() {
481+
service.setService({
482+
ReadRows: ReadRowsImpl.createService(
483+
TRANSIENT_ERROR_SERVICE
484+
) as ServerImplementationInterface,
485+
});
486+
487+
const rows = await table.getRows();
488+
return rows;
489+
}
490+
491+
try {
492+
await readRowsWithDeadline();
493+
assert.fail('Should have thrown error');
494+
} catch (err) {
495+
if (err instanceof GoogleError) {
496+
assert.equal(err.code, 'DEADLINE_EXCEEDED');
497+
}
498+
499+
// Assert that no retry attempted.
500+
assert.strictEqual(requests.length, 1);
501+
}
502+
});
503+
464504
after(async () => {
465505
server.shutdown(() => {});
466506
});

test/utils/readRowsImpl.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ export class ReadRowsImpl {
349349
*/
350350
private async handleRequest(stream: ReadRowsWritableStream) {
351351
const debugLog = this.serviceParameters.debugLog;
352+
const hook = this.serviceParameters.hook;
353+
if (hook) {
354+
hook(stream.request);
355+
}
356+
352357
prettyPrintRequest(stream.request, debugLog);
353358
const readRowsRequestHandler = new ReadRowsRequestHandler(stream, debugLog);
354359
stream.on('cancelled', () => {
@@ -374,12 +379,15 @@ export class ReadRowsImpl {
374379
) {
375380
const stream = readRowsRequestHandler.stream;
376381
const debugLog = readRowsRequestHandler.debugLog;
382+
const deadlineExceededError = this.serviceParameters.deadlineExceededError;
383+
377384
let chunksSent = 0;
378385
let lastScannedRowKey: string | undefined;
379386
let currentResponseChunks: protos.google.bigtable.v2.ReadRowsResponse.ICellChunk[] =
380387
[];
381388
let chunkIdx = 0;
382389
let skipThisRow = false;
390+
383391
for (const chunk of chunks) {
384392
if (readRowsRequestHandler.cancelled) {
385393
break;
@@ -409,19 +417,22 @@ export class ReadRowsImpl {
409417
currentResponseChunks.push(chunk);
410418
++chunkIdx;
411419
}
420+
412421
if (
413422
currentResponseChunks.length ===
414423
this.serviceParameters.chunksPerResponse ||
415424
chunkIdx === this.errorAfterChunkNo ||
416425
// if we skipped a row and set lastScannedRowKey, dump everything and send a separate message with lastScannedRowKey
417-
lastScannedRowKey
426+
lastScannedRowKey ||
427+
deadlineExceededError
418428
) {
419429
const response: protos.google.bigtable.v2.IReadRowsResponse = {
420430
chunks: currentResponseChunks,
421431
};
422432
chunksSent += currentResponseChunks.length;
423433
await readRowsRequestHandler.sendResponse(response);
424434
currentResponseChunks = [];
435+
425436
if (chunkIdx === this.errorAfterChunkNo) {
426437
debugLog(`sending error after chunk #${chunkIdx}`);
427438
this.errorAfterChunkNo = undefined; // do not send error for the second time
@@ -431,7 +442,17 @@ export class ReadRowsImpl {
431442
readRowsRequestHandler.cancelled = true;
432443
break;
433444
}
445+
446+
if (deadlineExceededError) {
447+
debugLog('sending deadline exceeded error');
448+
const error = new GoogleError('Deadline exceeded');
449+
error.code = Status.DEADLINE_EXCEEDED;
450+
stream.emit('error', error);
451+
readRowsRequestHandler.cancelled = true;
452+
break;
453+
}
434454
}
455+
435456
if (lastScannedRowKey) {
436457
const response: protos.google.bigtable.v2.IReadRowsResponse = {
437458
lastScannedRowKey,

test/utils/readRowsServiceParameters.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ interface SharedReadRowsParameters {
2727
export type DebugLog = (message: string) => void;
2828

2929
export interface ReadRowsServiceParameters extends SharedReadRowsParameters {
30+
deadlineExceededError?: boolean; // Send deadline exceeded transient error
31+
errorAfterChunkNo?: number; // The chunk that the error should come after
3032
keyFrom?: number; // The key the data coming from the service will start from
3133
keyTo?: number; // The key the data coming from the service will end at
32-
errorAfterChunkNo?: number; // The chunk that the error should come after
3334
chunksPerResponse: number; // The total number of chunks the server should send
3435
debugLog: DebugLog;
36+
hook?: (request: protos.google.bigtable.v2.IReadRowsRequest) => void;
3537
}
3638

3739
export interface ChunkGeneratorParameters extends SharedReadRowsParameters {

0 commit comments

Comments
 (0)