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

Commit 37f9b3c

Browse files
feat: send retry attempt header to ease debugging (#1068)
* feat: send retry attempt header to ease debugging * style * lint
1 parent 3a02e49 commit 37f9b3c

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

src/table.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,7 @@ Please use the format 'prezzy' or '${instance.name}/tables/prezzy'.`);
743743
const hasLimit = rowsLimit !== 0;
744744
let rowsRead = 0;
745745
let numConsecutiveErrors = 0;
746+
let numRequestsMade = 0;
746747
let retryTimer: NodeJS.Timeout | null;
747748

748749
rowKeys = options.keys || [];
@@ -917,6 +918,11 @@ Please use the format 'prezzy' or '${instance.name}/tables/prezzy'.`);
917918
reqOpts.rowsLimit = rowsLimit - rowsRead;
918919
}
919920

921+
options.gaxOptions = populateAttemptHeader(
922+
numRequestsMade,
923+
options.gaxOptions
924+
);
925+
920926
const requestStream = this.bigtable.request({
921927
client: 'BigtableClient',
922928
method: 'readRows',
@@ -970,6 +976,7 @@ Please use the format 'prezzy' or '${instance.name}/tables/prezzy'.`);
970976
return;
971977
}
972978
numConsecutiveErrors++;
979+
numRequestsMade++;
973980
if (
974981
numConsecutiveErrors <= maxRetries &&
975982
(RETRYABLE_STATUS_CODES.has(error.code) || isRstStreamError(error))
@@ -1614,6 +1621,11 @@ Please use the format 'prezzy' or '${instance.name}/tables/prezzy'.`);
16141621
},
16151622
};
16161623

1624+
options.gaxOptions = populateAttemptHeader(
1625+
numRequestsMade,
1626+
options.gaxOptions
1627+
);
1628+
16171629
this.bigtable
16181630
.request<google.bigtable.v2.MutateRowsResponse>({
16191631
client: 'BigtableClient',
@@ -2072,6 +2084,14 @@ function getNextDelay(numConsecutiveErrors: number, config: BackoffSettings) {
20722084
return Math.min(calculatedNextRetryDelay, config.maxRetryDelayMillis);
20732085
}
20742086

2087+
function populateAttemptHeader(attempt: number, gaxOpts?: CallOptions) {
2088+
gaxOpts = gaxOpts || {};
2089+
gaxOpts.otherArgs = gaxOpts.otherArgs || {};
2090+
gaxOpts.otherArgs.headers = gaxOpts.otherArgs.headers || {};
2091+
gaxOpts.otherArgs.headers['bigtable-attempt'] = attempt;
2092+
return gaxOpts;
2093+
}
2094+
20752095
export interface GoogleInnerError {
20762096
reason?: string;
20772097
message?: string;

test/table.ts

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {Family} from '../src/family.js';
2727
import {Mutation} from '../src/mutation.js';
2828
import {Row} from '../src/row.js';
2929
import * as tblTypes from '../src/table';
30-
import {Bigtable} from '../src';
30+
import {Bigtable, RequestOptions} from '../src';
3131
import {EventEmitter} from 'events';
3232

3333
const sandbox = sinon.createSandbox();
@@ -544,7 +544,9 @@ describe('Bigtable/Table', () => {
544544
assert.strictEqual(config.method, 'readRows');
545545
assert.strictEqual(config.reqOpts.tableName, TABLE_NAME);
546546
assert.strictEqual(config.reqOpts.appProfileId, undefined);
547-
assert.strictEqual(config.gaxOpts, undefined);
547+
assert.deepStrictEqual(config.gaxOpts, {
548+
otherArgs: {headers: {'bigtable-attempt': 0}},
549+
});
548550
done();
549551
};
550552
table.createReadStream();
@@ -2570,6 +2572,7 @@ describe('Bigtable/Table', () => {
25702572
let fakeStatuses: any;
25712573
// eslint-disable-next-line @typescript-eslint/no-explicit-any
25722574
let entryRequests: any;
2575+
const requestArgs: RequestOptions[] = [];
25732576

25742577
beforeEach(() => {
25752578
entryRequests = [];
@@ -2599,6 +2602,7 @@ describe('Bigtable/Table', () => {
25992602
];
26002603
// eslint-disable-next-line @typescript-eslint/no-explicit-any
26012604
table.bigtable.request = (config: any) => {
2605+
requestArgs.push(JSON.parse(JSON.stringify(config)));
26022606
entryRequests.push(config.reqOpts.entries);
26032607
const stream = new PassThrough({
26042608
objectMode: true,
@@ -2612,6 +2616,25 @@ describe('Bigtable/Table', () => {
26122616
};
26132617
});
26142618

2619+
it('should send attempt header', done => {
2620+
table.mutate(entries, () => {
2621+
assert.strictEqual(requestArgs.length, 2);
2622+
assert.strictEqual(
2623+
(requestArgs[0].gaxOpts as any)['otherArgs']['headers'][
2624+
'bigtable-attempt'
2625+
],
2626+
0
2627+
);
2628+
assert.strictEqual(
2629+
(requestArgs[1].gaxOpts as any)['otherArgs']['headers'][
2630+
'bigtable-attempt'
2631+
],
2632+
1
2633+
);
2634+
done();
2635+
});
2636+
});
2637+
26152638
it('should succeed after a retry', done => {
26162639
table.maxRetries = 1;
26172640
table.mutate(entries, done);
@@ -2630,17 +2653,20 @@ describe('Bigtable/Table', () => {
26302653

26312654
describe('rpc level retries', () => {
26322655
let emitters: EventEmitter[] | null; // = [((stream: Writable) => { stream.push([{ key: 'a' }]);
2656+
let requestArgs: RequestOptions[] = [];
26332657

26342658
// eslint-disable-next-line @typescript-eslint/no-explicit-any
26352659
let entryRequests: any;
26362660

26372661
beforeEach(() => {
26382662
emitters = null; // This needs to be assigned in each test case.
26392663

2664+
requestArgs = [];
26402665
entryRequests = [];
26412666

26422667
// eslint-disable-next-line @typescript-eslint/no-explicit-any
26432668
table.bigtable.request = (config: any) => {
2669+
requestArgs.push(JSON.parse(JSON.stringify(config)));
26442670
entryRequests.push(config.reqOpts.entries);
26452671
const stream = new PassThrough({
26462672
objectMode: true,
@@ -2707,6 +2733,36 @@ describe('Bigtable/Table', () => {
27072733
done();
27082734
});
27092735
});
2736+
2737+
it('should send attempt header', done => {
2738+
const error = new Error('retryable') as ServiceError;
2739+
error.code = 14; // Unavailable
2740+
emitters = [
2741+
((stream: Writable) => {
2742+
stream.emit('error', error);
2743+
}) as {} as EventEmitter,
2744+
((stream: Writable) => {
2745+
stream.end();
2746+
}) as {} as EventEmitter,
2747+
];
2748+
table.maxRetries = 1;
2749+
table.mutate(entries, () => {
2750+
assert.strictEqual(requestArgs.length, 2);
2751+
assert.strictEqual(
2752+
(requestArgs[0].gaxOpts as any)['otherArgs']['headers'][
2753+
'bigtable-attempt'
2754+
],
2755+
0
2756+
);
2757+
assert.strictEqual(
2758+
(requestArgs[1].gaxOpts as any)['otherArgs']['headers'][
2759+
'bigtable-attempt'
2760+
],
2761+
1
2762+
);
2763+
done();
2764+
});
2765+
});
27102766
});
27112767
});
27122768

0 commit comments

Comments
 (0)