# #node.js #google-cloud-spanner
Вопрос:
Заранее спасибо за любую помощь здесь.
Я пытаюсь установить время ожидания для каждого вызова в клиенте Spanner nodejs. Я прочитал данную документацию по гаечному ключу, лучшая из которых находится здесь: https://cloud.google.com/spanner/docs/custom-timeout-and-retry. В этой документации не указано, что вам необходимо предоставить gaxOptions
. Или я неправильно понял, что было бы неудивительно, учитывая, что я не могу объяснить поведение, которое вижу.
Я создал небольшое репо, чтобы разместить эту репродукцию здесь: https://github.com/brg8/spanner-nodejs-timeout-repro. Код также вставлен ниже.
const PROJECT_ID_HERE = "";
const SPANNER_INSTANCE_ID_HERE = "";
const SPANNER_DATABASE_HERE = "";
const TABLE_NAME_HERE = "";
const { Spanner } = require("@google-cloud/spanner");
let client = null;
client = new Spanner({
projectId: PROJECT_ID_HERE,
});
const instance = client.instance(SPANNER_INSTANCE_ID_HERE);
const database = instance.database(SPANNER_DATABASE_HERE);
async function runQuery(additionalOptions = {}) {
const t1 = new Date();
try {
console.log("Launching query...");
await database.run({
sql: `SELECT * FROM ${TABLE_NAME_HERE} LIMIT 1000;`,
...additionalOptions,
});
console.log("Everything finished.");
} catch (err) {
console.log(err);
console.log("Timed out after", new Date() - t1);
}
};
// Query finishes, no timeout (as expected)
runQuery();
/*
Launching query...
Everything finished.
*/
// Query times out (as expected)
// However, it only times out after 7-8 seconds
runQuery({
gaxOptions: {
timeout: 1,
},
});
/*
Launching query...
Error: 4 DEADLINE_EXCEEDED: Deadline exceeded
at Object.callErrorFromStatus (/Users/benjamingodlove/Developer/spanner-node-repro/node_modules/@grpc/grpc-js/build/src/call.js:31:26)
at Object.onReceiveStatus (/Users/benjamingodlove/Developer/spanner-node-repro/node_modules/@grpc/grpc-js/build/src/client.js:330:49)
at /Users/benjamingodlove/Developer/spanner-node-repro/node_modules/@grpc/grpc-js/build/src/call-stream.js:80:35
at Object.onReceiveStatus (/Users/benjamingodlove/Developer/spanner-node-repro/node_modules/grpc-gcp/build/src/index.js:73:29)
at InterceptingListenerImpl.onReceiveStatus (/Users/benjamingodlove/Developer/spanner-node-repro/node_modules/@grpc/grpc-js/build/src/call-stream.js:75:23)
at Object.onReceiveStatus (/Users/benjamingodlove/Developer/spanner-node-repro/node_modules/@grpc/grpc-js/build/src/client-interceptors.js:299:181)
at /Users/benjamingodlove/Developer/spanner-node-repro/node_modules/@grpc/grpc-js/build/src/call-stream.js:145:78
at processTicksAndRejections (node:internal/process/task_queues:76:11) {
code: 4,
details: 'Deadline exceeded',
metadata: Metadata { internalRepr: Map(0) {}, options: {} }
}
Timed out after 7238
*/
И мой пакет.json
{
"name": "spanner-node-repro",
"version": "1.0.0",
"description": "Reproducing timeout wonkiness with Spanner.",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" amp;amp; exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@google-cloud/spanner": "^5.15.2"
}
}
Любое понимание ценится!
- Бен Годлав
Ответ №1:
TLDR
Добавьте retryRequestOptions: {noResponseRetries: 0},
в свой gaxOptions
, чтобы вы получили следующие опции:
const query = {
sql: 'SELECT ...',
gaxOptions: {
timeout: 1,
retryRequestOptions: {noResponseRetries: 0},
},
};
Более длинная Версия
То, что происходит под капотом, заключается в следующем:
- Запрос (потоковый) запрос отправляется, и время ожидания истекает до того, как сервер вернет какой-либо ответ.
- Настройки повторной попытки по умолчанию включают
noResponseRetries: 2
опцию, которая означает, что запрос будет повторен дважды, если клиент вообще не получил никакого ответа. - Повторная попытка запроса начнется только после случайной задержки повторной попытки. Эта задержка также увеличивается при каждой повторной попытке.
- После повторной попытки дважды (таким образом, после отправки в общей сложности 3 запросов) ошибка DEADLINE_EXCEEDED будет передана клиенту. Эти повторные попытки занимают примерно 7 секунд, потому что первая повторная попытка занимает около 2,5 секунд, а вторая-4,5 секунды (оба значения содержат случайное значение дрожания в 1 секунду, поэтому фактическое значение всегда будет между 6 и 8 секундами).
Этот параметр noResponseRetries: 0
отключает повторные попытки запросов, которые не получают ответа от сервера.
Вы также увидите, что если вы установите тайм-аут на более «разумное» значение, вы увидите, что время ожидания запроса истекло в обычном режиме, так как у сервера есть шанс ответить. Установка его на что-то вроде 1500 (то есть 1500 мс, то есть 1,5 секунды) приводит к тому, что тайм-аут работает так, как я ожидал, используя ваш пример кода.
Комментарии:
1. Ваше решение по использованию
noResponseRetries
работает для меня! Я хочу отметить, однако, что без использованияnoResponseRetries
я получаю увеличенные тайм-ауты даже при более высоких значениях (например, 1,5 секунды). Я подозреваю, потому что выполнение запроса занимает несколько минут.