feat(vhd-lib/mergeVhd): continuable (#5749)
This commit is contained in:
parent
e6f8fd9234
commit
aa4f1b834a
@ -3,10 +3,14 @@
|
||||
import assert from 'assert'
|
||||
import concurrency from 'limit-concurrency-decorator'
|
||||
import noop from './_noop'
|
||||
import { createLogger } from '@xen-orchestra/log'
|
||||
|
||||
import Vhd from './vhd'
|
||||
import { basename, dirname } from 'path'
|
||||
import { DISK_TYPE_DIFFERENCING, DISK_TYPE_DYNAMIC } from './_constants'
|
||||
|
||||
const { warn } = createLogger('vhd-lib:merge')
|
||||
|
||||
// Merge vhd child into vhd parent.
|
||||
//
|
||||
// TODO: rename the VHD file during the merge
|
||||
@ -17,6 +21,8 @@ export default concurrency(2)(async function merge(
|
||||
childPath,
|
||||
{ onProgress = noop } = {}
|
||||
) {
|
||||
const mergeStatePath = dirname(parentPath) + '/' + '.' + basename(parentPath) + '.merge.json'
|
||||
|
||||
const parentFd = await parentHandler.openFile(parentPath, 'r+')
|
||||
try {
|
||||
const parentVhd = new Vhd(parentHandler, parentFd)
|
||||
@ -24,31 +30,61 @@ export default concurrency(2)(async function merge(
|
||||
try {
|
||||
const childVhd = new Vhd(childHandler, childFd)
|
||||
|
||||
let mergeState = await parentHandler.readFile(mergeStatePath).catch(error => {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error
|
||||
}
|
||||
// no merge state in case of missing file
|
||||
})
|
||||
|
||||
// Reading footer and header.
|
||||
await Promise.all([parentVhd.readHeaderAndFooter(), childVhd.readHeaderAndFooter()])
|
||||
await Promise.all([
|
||||
parentVhd.readHeaderAndFooter(
|
||||
// dont check VHD is complete if recovering a merge
|
||||
mergeState === undefined
|
||||
),
|
||||
|
||||
assert(childVhd.header.blockSize === parentVhd.header.blockSize)
|
||||
childVhd.readHeaderAndFooter(),
|
||||
])
|
||||
|
||||
const parentDiskType = parentVhd.footer.diskType
|
||||
assert(parentDiskType === DISK_TYPE_DIFFERENCING || parentDiskType === DISK_TYPE_DYNAMIC)
|
||||
assert.strictEqual(childVhd.footer.diskType, DISK_TYPE_DIFFERENCING)
|
||||
if (mergeState !== undefined) {
|
||||
mergeState = JSON.parse(mergeState)
|
||||
|
||||
// ensure the correct merge will be continued
|
||||
assert.strictEqual(parentVhd.header.checksum, mergeState.parent.header)
|
||||
assert.strictEqual(childVhd.header.checksum, mergeState.child.header)
|
||||
} else {
|
||||
assert.strictEqual(childVhd.header.blockSize, parentVhd.header.blockSize)
|
||||
|
||||
const parentDiskType = parentVhd.footer.diskType
|
||||
assert(parentDiskType === DISK_TYPE_DIFFERENCING || parentDiskType === DISK_TYPE_DYNAMIC)
|
||||
assert.strictEqual(childVhd.footer.diskType, DISK_TYPE_DIFFERENCING)
|
||||
}
|
||||
|
||||
// Read allocation table of child/parent.
|
||||
await Promise.all([parentVhd.readBlockAllocationTable(), childVhd.readBlockAllocationTable()])
|
||||
|
||||
const { maxTableEntries } = childVhd.header
|
||||
|
||||
await parentVhd.ensureBatSize(childVhd.header.maxTableEntries)
|
||||
if (mergeState === undefined) {
|
||||
await parentVhd.ensureBatSize(childVhd.header.maxTableEntries)
|
||||
|
||||
// finds first allocated block for the 2 following loops
|
||||
let firstBlock = 0
|
||||
while (firstBlock < maxTableEntries && !childVhd.containsBlock(firstBlock)) {
|
||||
++firstBlock
|
||||
mergeState = {
|
||||
child: { header: childVhd.header.checksum },
|
||||
parent: { header: parentVhd.header.checksum },
|
||||
currentBlock: 0,
|
||||
mergedDataSize: 0,
|
||||
}
|
||||
|
||||
// finds first allocated block for the 2 following loops
|
||||
while (mergeState.currentBlock < maxTableEntries && !childVhd.containsBlock(mergeState.currentBlock)) {
|
||||
++mergeState.currentBlock
|
||||
}
|
||||
}
|
||||
|
||||
// counts number of allocated blocks
|
||||
let nBlocks = 0
|
||||
for (let block = firstBlock; block < maxTableEntries; block++) {
|
||||
for (let block = mergeState.currentBlock; block < maxTableEntries; block++) {
|
||||
if (childVhd.containsBlock(block)) {
|
||||
nBlocks += 1
|
||||
}
|
||||
@ -57,13 +93,14 @@ export default concurrency(2)(async function merge(
|
||||
onProgress({ total: nBlocks, done: 0 })
|
||||
|
||||
// merges blocks
|
||||
let mergedDataSize = 0
|
||||
for (let i = 0, block = firstBlock; i < nBlocks; ++i, ++block) {
|
||||
while (!childVhd.containsBlock(block)) {
|
||||
++block
|
||||
for (let i = 0; i < nBlocks; ++i, ++mergeState.currentBlock) {
|
||||
while (!childVhd.containsBlock(mergeState.currentBlock)) {
|
||||
++mergeState.currentBlock
|
||||
}
|
||||
|
||||
mergedDataSize += await parentVhd.coalesceBlock(childVhd, block)
|
||||
await parentHandler.writeFile(mergeStatePath, JSON.stringify(mergeState), { flags: 'w' }).catch(warn)
|
||||
|
||||
mergeState.mergedDataSize += await parentVhd.coalesceBlock(childVhd, mergeState.currentBlock)
|
||||
onProgress({
|
||||
total: nBlocks,
|
||||
done: i + 1,
|
||||
@ -83,11 +120,12 @@ export default concurrency(2)(async function merge(
|
||||
// creation
|
||||
await parentVhd.writeFooter()
|
||||
|
||||
return mergedDataSize
|
||||
return mergeState.mergedDataSize
|
||||
} finally {
|
||||
await childHandler.closeFile(childFd)
|
||||
}
|
||||
} finally {
|
||||
parentHandler.unlink(mergeStatePath).catch(warn)
|
||||
await parentHandler.closeFile(parentFd)
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user