Merge pull request #2405 from balena-os/fix-api-request-timeout

Decrease balenaCloud api request timeout from 15m to 59s
This commit is contained in:
Page- 2025-03-04 13:34:35 +00:00 committed by GitHub
commit 906ce6dc0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 54 additions and 22 deletions

View File

@ -101,11 +101,11 @@ export const update = async (
): Promise<void> => {
await config.initialized();
return Bluebird.using(lockGetTarget(), async () => {
const { uuid, apiEndpoint, apiTimeout, deviceApiKey } =
const { uuid, apiEndpoint, apiRequestTimeout, deviceApiKey } =
await config.getMany([
'uuid',
'apiEndpoint',
'apiTimeout',
'apiRequestTimeout',
'deviceApiKey',
]);
@ -126,12 +126,12 @@ export const update = async (
timeout: {
// TODO: We use the same default timeout for all of these in order to have a timeout generally
// but it would probably make sense to tune them individually
lookup: apiTimeout,
connect: apiTimeout,
secureConnect: apiTimeout,
socket: apiTimeout,
send: apiTimeout,
response: apiTimeout,
lookup: apiRequestTimeout,
connect: apiRequestTimeout,
secureConnect: apiRequestTimeout,
socket: apiRequestTimeout,
send: apiRequestTimeout,
response: apiRequestTimeout,
},
});

View File

@ -41,14 +41,17 @@ export let stateReportErrors = 0;
type StateReportOpts = {
[key in keyof Pick<
config.ConfigMap<SchemaTypeKey>,
'apiEndpoint' | 'apiTimeout' | 'deviceApiKey' | 'appUpdatePollInterval'
| 'apiEndpoint'
| 'apiRequestTimeout'
| 'deviceApiKey'
| 'appUpdatePollInterval'
>]: SchemaReturn<key>;
};
type StateReport = { body: Partial<DeviceState>; opts: StateReportOpts };
async function report({ body, opts }: StateReport) {
const { apiEndpoint, apiTimeout, deviceApiKey } = opts;
const { apiEndpoint, apiRequestTimeout, deviceApiKey } = opts;
if (!apiEndpoint) {
throw new InternalInconsistencyError(
@ -69,7 +72,7 @@ async function report({ body, opts }: StateReport) {
const [{ statusCode, body: statusMessage, headers }] = await request
.patchAsync(endpoint, params)
.timeout(apiTimeout);
.timeout(apiRequestTimeout);
if (statusCode < 200 || statusCode >= 300) {
throw new StatusError(
@ -203,7 +206,7 @@ export async function startReporting() {
// Get configs needed to make a report
const reportConfigs = (await config.getMany([
'apiEndpoint',
'apiTimeout',
'apiRequestTimeout',
'deviceApiKey',
'appUpdatePollInterval',
])) as StateReportOpts;

View File

@ -90,7 +90,7 @@ export const fnSchema = {
'deviceArch',
'deviceType',
'apiEndpoint',
'apiTimeout',
'apiRequestTimeout',
'registered_at',
'deviceId',
'version',
@ -107,7 +107,7 @@ export const fnSchema = {
provisioningApiKey: conf.apiKey,
deviceApiKey: conf.deviceApiKey,
apiEndpoint: conf.apiEndpoint,
apiTimeout: conf.apiTimeout,
apiRequestTimeout: conf.apiRequestTimeout,
registered_at: conf.registered_at,
deviceId: conf.deviceId,
supervisorVersion: conf.version,

View File

@ -12,6 +12,9 @@ export const schemaTypes = {
type: t.string,
default: '',
},
/**
* The timeout for the supervisor's api
*/
apiTimeout: {
type: PermissiveNumber,
default: 15 * 60 * 1000,
@ -118,6 +121,13 @@ export const schemaTypes = {
type: PermissiveBoolean,
default: false,
},
/**
* The timeout for requests to the balenaCloud api
*/
apiRequestTimeout: {
type: PermissiveNumber,
default: 59000,
},
deltaRequestTimeout: {
type: PermissiveNumber,
default: 59000,
@ -218,7 +228,7 @@ export const schemaTypes = {
provisioningApiKey: t.union([t.string, NullOrUndefined]),
deviceApiKey: t.string,
apiEndpoint: t.string,
apiTimeout: PermissiveNumber,
apiRequestTimeout: PermissiveNumber,
registered_at: t.union([PermissiveNumber, NullOrUndefined]),
deviceId: t.union([PermissiveNumber, NullOrUndefined]),
supervisorVersion: t.union([t.string, t.undefined]),

View File

@ -4,6 +4,9 @@ export const schema = {
mutable: false,
removeIfNull: false,
},
/**
* The timeout for the supervisor's api
*/
apiTimeout: {
source: 'config.json',
mutable: false,
@ -120,6 +123,11 @@ export const schema = {
mutable: true,
removeIfNull: false,
},
apiRequestTimeout: {
source: 'db',
mutable: true,
removeIfNull: false,
},
delta: {
source: 'db',
mutable: true,

View File

@ -141,6 +141,11 @@ const configKeys: Dictionary<ConfigOption> = {
varType: 'bool',
defaultValue: 'true',
},
apiRequestTimeout: {
envVarName: 'SUPERVISOR_API_REQUEST_TIMEOUT',
varType: 'int',
defaultValue: '59000',
},
delta: {
envVarName: 'SUPERVISOR_DELTA',
varType: 'bool',

View File

@ -111,10 +111,10 @@ export const exchangeKeyAndGetDevice = async (
opts: Partial<KeyExchangeOpts>,
): Promise<Device> => {
const uuid = opts.uuid;
const apiTimeout = opts.apiTimeout;
if (!(uuid && apiTimeout)) {
const apiRequestTimeout = opts.apiRequestTimeout;
if (!(uuid && apiRequestTimeout)) {
throw new InternalInconsistencyError(
'UUID and apiTimeout should be defined in exchangeKeyAndGetDevice',
'UUID and apiRequestTimeout should be defined in exchangeKeyAndGetDevice',
);
}
@ -122,7 +122,12 @@ export const exchangeKeyAndGetDevice = async (
// valid, because if it is then we can just use that
if (opts.deviceApiKey != null) {
try {
return await fetchDevice(balenaApi, uuid, opts.deviceApiKey, apiTimeout);
return await fetchDevice(
balenaApi,
uuid,
opts.deviceApiKey,
apiRequestTimeout,
);
} catch (e) {
if (e instanceof DeviceNotFoundError) {
// do nothing...
@ -146,7 +151,7 @@ export const exchangeKeyAndGetDevice = async (
balenaApi,
uuid,
opts.provisioningApiKey,
apiTimeout,
apiRequestTimeout,
);
} catch {
throw new ExchangeKeyError(`Couldn't fetch device with provisioning key`);
@ -165,7 +170,7 @@ export const exchangeKeyAndGetDevice = async (
Authorization: `Bearer ${opts.provisioningApiKey}`,
},
})
.timeout(apiTimeout);
.timeout(apiRequestTimeout);
if (res.statusCode !== 200) {
throw new ExchangeKeyError(
@ -220,7 +225,7 @@ export const provision = async (
osVariant: opts.osVariant,
macAddress: opts.macAddress,
}),
).timeout(opts.apiTimeout);
).timeout(opts.apiRequestTimeout);
} catch (err) {
if (
err instanceof deviceRegister.ApiError &&

View File

@ -84,6 +84,7 @@ describe('device-config', () => {
SUPERVISOR_LOCAL_MODE: 'false',
SUPERVISOR_CONNECTIVITY_CHECK: 'true',
SUPERVISOR_LOG_CONTROL: 'true',
SUPERVISOR_API_REQUEST_TIMEOUT: '59000',
SUPERVISOR_DELTA: 'false',
SUPERVISOR_DELTA_REQUEST_TIMEOUT: '59000',
SUPERVISOR_DELTA_APPLY_TIMEOUT: '0',