Установка тайм-аута для каждого вызова в nodejs

# #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},
  },
};
 

Более длинная Версия

То, что происходит под капотом, заключается в следующем:

  1. Запрос (потоковый) запрос отправляется, и время ожидания истекает до того, как сервер вернет какой-либо ответ.
  2. Настройки повторной попытки по умолчанию включают noResponseRetries: 2 опцию, которая означает, что запрос будет повторен дважды, если клиент вообще не получил никакого ответа.
  3. Повторная попытка запроса начнется только после случайной задержки повторной попытки. Эта задержка также увеличивается при каждой повторной попытке.
  4. После повторной попытки дважды (таким образом, после отправки в общей сложности 3 запросов) ошибка DEADLINE_EXCEEDED будет передана клиенту. Эти повторные попытки занимают примерно 7 секунд, потому что первая повторная попытка занимает около 2,5 секунд, а вторая-4,5 секунды (оба значения содержат случайное значение дрожания в 1 секунду, поэтому фактическое значение всегда будет между 6 и 8 секундами).

Этот параметр noResponseRetries: 0 отключает повторные попытки запросов, которые не получают ответа от сервера.

Вы также увидите, что если вы установите тайм-аут на более «разумное» значение, вы увидите, что время ожидания запроса истекло в обычном режиме, так как у сервера есть шанс ответить. Установка его на что-то вроде 1500 (то есть 1500 мс, то есть 1,5 секунды) приводит к тому, что тайм-аут работает так, как я ожидал, используя ваш пример кода.

Комментарии:

1. Ваше решение по использованию noResponseRetries работает для меня! Я хочу отметить, однако, что без использования noResponseRetries я получаю увеличенные тайм-ауты даже при более высоких значениях (например, 1,5 секунды). Я подозреваю, потому что выполнение запроса занимает несколько минут.