feat(xo-server/file restore): dedupe mount/unmount (#4961)
This commit is contained in:
parent
6555e2c440
commit
48ce7df43a
45
packages/xo-server/src/_dedupeUnmount.js
Normal file
45
packages/xo-server/src/_dedupeUnmount.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import assert from 'assert'
|
||||||
|
|
||||||
|
import ensureArray from './_ensureArray'
|
||||||
|
import MultiKeyMap from './_MultiKeyMap'
|
||||||
|
|
||||||
|
function State() {
|
||||||
|
this.i = 0
|
||||||
|
this.value = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dedupeUnmount = (fn, keyFn) => {
|
||||||
|
const states = new MultiKeyMap()
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
const keys = ensureArray(keyFn.apply(this, arguments))
|
||||||
|
let state = states.get(keys)
|
||||||
|
if (state === undefined) {
|
||||||
|
state = new State()
|
||||||
|
states.set(keys, state)
|
||||||
|
|
||||||
|
const mount = async () => {
|
||||||
|
try {
|
||||||
|
const value = await fn.apply(this, arguments)
|
||||||
|
return {
|
||||||
|
__proto__: value,
|
||||||
|
async unmount() {
|
||||||
|
assert(state.i > 0)
|
||||||
|
if (--state.i === 0) {
|
||||||
|
states.delete(keys)
|
||||||
|
await value.unmount()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
states.delete(keys)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state.value = mount()
|
||||||
|
}
|
||||||
|
++state.i
|
||||||
|
return state.value
|
||||||
|
}
|
||||||
|
}
|
@ -6,9 +6,15 @@ import { normalize } from 'path'
|
|||||||
import { readdir, rmdir, stat } from 'fs-extra'
|
import { readdir, rmdir, stat } from 'fs-extra'
|
||||||
import { ZipFile } from 'yazl'
|
import { ZipFile } from 'yazl'
|
||||||
|
|
||||||
|
import { decorateWith } from '../_decorateWith'
|
||||||
|
import { dedupeUnmount } from '../_dedupeUnmount'
|
||||||
import { lvs, pvs } from '../lvm'
|
import { lvs, pvs } from '../lvm'
|
||||||
import { resolveSubpath, tmpDir } from '../utils'
|
import { resolveSubpath, tmpDir } from '../utils'
|
||||||
|
|
||||||
|
const compose = (...fns) => value => fns.reduce((value, fn) => fn(value), value)
|
||||||
|
|
||||||
|
const dedupeUnmountWithArgs = fn => dedupeUnmount(fn, (...args) => args)
|
||||||
|
|
||||||
const IGNORED_PARTITION_TYPES = {
|
const IGNORED_PARTITION_TYPES = {
|
||||||
// https://github.com/jhermsmeier/node-mbr/blob/master/lib/partition.js#L38
|
// https://github.com/jhermsmeier/node-mbr/blob/master/lib/partition.js#L38
|
||||||
0x05: true,
|
0x05: true,
|
||||||
@ -60,8 +66,10 @@ const parsePartxLine = createPairsParser({
|
|||||||
: value,
|
: value,
|
||||||
})
|
})
|
||||||
|
|
||||||
const listLvmLogicalVolumes = defer(
|
const listLvmLogicalVolumes = compose(
|
||||||
async ($defer, devicePath, partition, results = []) => {
|
defer,
|
||||||
|
dedupeUnmountWithArgs
|
||||||
|
)(async ($defer, devicePath, partition, results = []) => {
|
||||||
const pv = await mountLvmPhysicalVolume(devicePath, partition)
|
const pv = await mountLvmPhysicalVolume(devicePath, partition)
|
||||||
$defer(pv.unmount)
|
$defer(pv.unmount)
|
||||||
|
|
||||||
@ -78,10 +86,10 @@ const listLvmLogicalVolumes = defer(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
return results
|
return results
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
async function mountLvmPhysicalVolume(devicePath, partition) {
|
const mountLvmPhysicalVolume = dedupeUnmountWithArgs(
|
||||||
|
async (devicePath, partition) => {
|
||||||
const args = []
|
const args = []
|
||||||
if (partition !== undefined) {
|
if (partition !== undefined) {
|
||||||
args.push('-o', partition.start * 512)
|
args.push('-o', partition.start * 512)
|
||||||
@ -101,9 +109,13 @@ async function mountLvmPhysicalVolume(devicePath, partition) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const mountPartition = defer(async ($defer, devicePath, partition) => {
|
const mountPartition = compose(
|
||||||
|
defer,
|
||||||
|
dedupeUnmountWithArgs
|
||||||
|
)(async ($defer, devicePath, partition) => {
|
||||||
const options = ['loop', 'ro']
|
const options = ['loop', 'ro']
|
||||||
|
|
||||||
if (partition !== undefined) {
|
if (partition !== undefined) {
|
||||||
@ -280,6 +292,7 @@ export default class BackupNgFileRestore {
|
|||||||
return partitions
|
return partitions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@decorateWith(dedupeUnmountWithArgs)
|
||||||
@defer
|
@defer
|
||||||
async _mountDisk($defer, remoteId, diskId) {
|
async _mountDisk($defer, remoteId, diskId) {
|
||||||
const handler = await this._app.getRemoteHandler(remoteId)
|
const handler = await this._app.getRemoteHandler(remoteId)
|
||||||
@ -321,6 +334,7 @@ export default class BackupNgFileRestore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@decorateWith(dedupeUnmountWithArgs)
|
||||||
@defer
|
@defer
|
||||||
async _mountPartition($defer, devicePath, partitionId) {
|
async _mountPartition($defer, devicePath, partitionId) {
|
||||||
if (partitionId === undefined) {
|
if (partitionId === undefined) {
|
||||||
|
Loading…
Reference in New Issue
Block a user