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