mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-03-23 12:35:44 +00:00
Separate rwlock functionality from update-lock for clarity
Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
parent
d18a740a40
commit
7cfc42e197
@ -1,19 +1,17 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
import * as config from '../config';
|
||||
|
||||
import type { Image } from './images';
|
||||
import * as images from './images';
|
||||
import type Network from './network';
|
||||
import type Service from './service';
|
||||
import * as serviceManager from './service-manager';
|
||||
import type Volume from './volume';
|
||||
|
||||
import { checkTruthy } from '../lib/validation';
|
||||
import * as networkManager from './network-manager';
|
||||
import * as volumeManager from './volume-manager';
|
||||
import type { DeviceLegacyReport } from '../types/state';
|
||||
import type Volume from './volume';
|
||||
import * as commitStore from './commit';
|
||||
import { checkTruthy } from '../lib/validation';
|
||||
import type { DeviceLegacyReport } from '../types/state';
|
||||
|
||||
interface BaseCompositionStepArgs {
|
||||
force?: boolean;
|
||||
|
@ -4,7 +4,7 @@ import _ from 'lodash';
|
||||
import * as constants from '../lib/constants';
|
||||
import * as hostUtils from '../lib/host-utils';
|
||||
import * as osRelease from '../lib/os-release';
|
||||
import { readLock, writeLock } from '../lib/update-lock';
|
||||
import { takeGlobalLockRO, takeGlobalLockRW } from '../lib/process-lock';
|
||||
import type * as Schema from './schema';
|
||||
|
||||
export default class ConfigJsonConfigBackend {
|
||||
@ -25,9 +25,9 @@ export default class ConfigJsonConfigBackend {
|
||||
this.schema = schema;
|
||||
|
||||
this.writeLockConfigJson = () =>
|
||||
writeLock('config.json').disposer((release) => release());
|
||||
takeGlobalLockRW('config.json').disposer((release) => release());
|
||||
this.readLockConfigJson = () =>
|
||||
readLock('config.json').disposer((release) => release());
|
||||
takeGlobalLockRO('config.json').disposer((release) => release());
|
||||
}
|
||||
|
||||
public async set<T extends Schema.SchemaKey>(keyVals: {
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
UpdatesLockedError,
|
||||
} from './lib/errors';
|
||||
import * as updateLock from './lib/update-lock';
|
||||
import { takeGlobalLockRO, takeGlobalLockRW } from './lib/process-lock';
|
||||
import * as dbFormat from './device-state/db-format';
|
||||
import { getGlobalApiKey } from './device-api';
|
||||
import * as sysInfo from './lib/system-info';
|
||||
@ -101,8 +102,6 @@ type DeviceStateStep<T extends PossibleStepTargets> =
|
||||
| deviceConfig.ConfigStep;
|
||||
|
||||
let currentVolatile: DeviceReport = {};
|
||||
const writeLock = updateLock.writeLock;
|
||||
const readLock = updateLock.readLock;
|
||||
let maxPollTime: number;
|
||||
let intermediateTarget: InstancedDeviceState | null = null;
|
||||
let applyBlocker: Nullable<Promise<void>>;
|
||||
@ -295,11 +294,11 @@ function emitAsync<T extends keyof DeviceStateEvents>(
|
||||
}
|
||||
|
||||
const readLockTarget = () =>
|
||||
readLock('target').disposer((release) => release());
|
||||
takeGlobalLockRO('target').disposer((release) => release());
|
||||
const writeLockTarget = () =>
|
||||
writeLock('target').disposer((release) => release());
|
||||
takeGlobalLockRW('target').disposer((release) => release());
|
||||
const inferStepsLock = () =>
|
||||
writeLock('inferSteps').disposer((release) => release());
|
||||
takeGlobalLockRW('inferSteps').disposer((release) => release());
|
||||
function usingReadLockTarget<T extends () => any, U extends ReturnType<T>>(
|
||||
fn: T,
|
||||
): Bluebird<UnwrappedPromise<U>> {
|
||||
@ -803,7 +802,7 @@ export const applyTarget = async ({
|
||||
|
||||
function pausingApply(fn: () => any) {
|
||||
const lock = () => {
|
||||
return writeLock('pause').disposer((release) => release());
|
||||
return takeGlobalLockRW('pause').disposer((release) => release());
|
||||
};
|
||||
// TODO: This function is a bit of a mess
|
||||
const pause = () => {
|
||||
|
@ -8,7 +8,7 @@ import type { TargetState } from '../types/state';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
import { getGotInstance } from '../lib/request';
|
||||
import * as config from '../config';
|
||||
import { writeLock } from '../lib/update-lock';
|
||||
import { takeGlobalLockRW } from '../lib/process-lock';
|
||||
import * as constants from '../lib/constants';
|
||||
import log from '../lib/supervisor-console';
|
||||
|
||||
@ -26,7 +26,7 @@ export const emitter: StrictEventEmitter<EventEmitter, TargetStateEvents> =
|
||||
new EventEmitter();
|
||||
|
||||
const lockGetTarget = () =>
|
||||
writeLock('getTarget').disposer((release) => release());
|
||||
takeGlobalLockRW('getTarget').disposer((release) => release());
|
||||
|
||||
type CachedResponse = {
|
||||
etag?: string | string[];
|
||||
|
35
src/lib/process-lock.ts
Normal file
35
src/lib/process-lock.ts
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This module contains the functionality for locking & unlocking resources
|
||||
* within the Supervisor Node process, useful for methods that need to acquire
|
||||
* exclusive access to a resource across multiple ticks in the event loop, async
|
||||
* functions for example.
|
||||
*
|
||||
* It is different from lockfile and update-lock modules, which handle
|
||||
* inter-container communication via lockfiles.
|
||||
*
|
||||
* TODO:
|
||||
* - Use a maintained solution such as async-lock
|
||||
* - Move to native Promises
|
||||
*/
|
||||
|
||||
import Bluebird from 'bluebird';
|
||||
import Lock from 'rwlock';
|
||||
import type { Release } from 'rwlock';
|
||||
|
||||
type LockFn = (key: string | number) => Bluebird<Release>;
|
||||
|
||||
const locker = new Lock();
|
||||
|
||||
export const takeGlobalLockRW: LockFn = Bluebird.promisify(
|
||||
locker.async.writeLock,
|
||||
{
|
||||
context: locker,
|
||||
},
|
||||
);
|
||||
|
||||
export const takeGlobalLockRO: LockFn = Bluebird.promisify(
|
||||
locker.async.readLock,
|
||||
{
|
||||
context: locker,
|
||||
},
|
||||
);
|
@ -1,7 +1,5 @@
|
||||
import Bluebird from 'bluebird';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
import Lock from 'rwlock';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
|
||||
import {
|
||||
@ -13,6 +11,7 @@ import { pathOnRoot, pathExistsOnState } from './host-utils';
|
||||
import * as config from '../config';
|
||||
import * as lockfile from './lockfile';
|
||||
import { NumericIdentifier, StringIdentifier, DockerName } from '../types';
|
||||
import { takeGlobalLockRW } from './process-lock';
|
||||
|
||||
const decodedUid = NumericIdentifier.decode(process.env.LOCKFILE_UID);
|
||||
export const LOCKFILE_UID = isRight(decodedUid) ? decodedUid.right : 65534;
|
||||
@ -56,15 +55,6 @@ export function abortIfHUPInProgress({
|
||||
});
|
||||
}
|
||||
|
||||
type LockFn = (key: string | number) => Bluebird<() => void>;
|
||||
const locker = new Lock();
|
||||
export const writeLock: LockFn = Bluebird.promisify(locker.async.writeLock, {
|
||||
context: locker,
|
||||
});
|
||||
export const readLock: LockFn = Bluebird.promisify(locker.async.readLock, {
|
||||
context: locker,
|
||||
});
|
||||
|
||||
// Unlock all lockfiles of an appId | appUuid, then release resources.
|
||||
async function dispose(
|
||||
appIdentifier: string | number,
|
||||
@ -195,7 +185,7 @@ export async function lock<T>(
|
||||
for (const id of sortedIds) {
|
||||
const lockDir = pathOnRoot(lockPath(id));
|
||||
// Acquire write lock for appId
|
||||
releases.set(id, await writeLock(id));
|
||||
releases.set(id, await takeGlobalLockRW(id));
|
||||
// Get list of service folders in lock directory
|
||||
const serviceFolders = await fs.readdir(lockDir).catch((e) => {
|
||||
if (ENOENT(e)) {
|
||||
|
@ -5,7 +5,7 @@ import * as config from './config';
|
||||
import * as db from './db';
|
||||
import * as eventTracker from './event-tracker';
|
||||
import type { LogType } from './lib/log-types';
|
||||
import { writeLock } from './lib/update-lock';
|
||||
import { takeGlobalLockRW } from './lib/process-lock';
|
||||
import type { LogBackend, LogMessage } from './logging';
|
||||
import { BalenaLogBackend, LocalLogBackend } from './logging';
|
||||
import type { MonitorHook } from './logging/monitor';
|
||||
@ -129,7 +129,7 @@ export function logSystemMessage(
|
||||
}
|
||||
|
||||
export function lock(containerId: string): Bluebird.Disposer<() => void> {
|
||||
return writeLock(containerId).disposer((release) => {
|
||||
return takeGlobalLockRW(containerId).disposer((release) => {
|
||||
release();
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user