Decrease balenaCloud api request timeout from 15m to 59s

This was mistakenly increased due to confusion between the timeout for
requests to the supervisor's api vs the timeout for requests from the
supervisor to the balenaCloud api. This separates the two configs and
documents the difference between the timeouts whilst also decreasing
the timeout for balenaCloud api requests to the correct/expected value

Change-type: patch
This commit is contained in:
Pagan Gazzard 2025-02-27 12:07:19 +00:00
parent f67e45f432
commit 49163e92a0
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',