mirror of
https://github.com/hcengineering/platform.git
synced 2024-11-26 13:47:26 +03:00
fix: add limit and retries to move files tool (#6368)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
parent
df2a9b2708
commit
56ac8eb4f1
@ -1044,7 +1044,8 @@ export function devTool (
|
|||||||
program
|
program
|
||||||
.command('move-files')
|
.command('move-files')
|
||||||
.option('-w, --workspace <workspace>', 'Selected workspace only', '')
|
.option('-w, --workspace <workspace>', 'Selected workspace only', '')
|
||||||
.action(async (cmd: { workspace: string }) => {
|
.option('-bl, --blobLimit <blobLimit>', 'A blob size limit in megabytes (default 50mb)', '50')
|
||||||
|
.action(async (cmd: { workspace: string, blobLimit: string }) => {
|
||||||
const { mongodbUri } = prepareTools()
|
const { mongodbUri } = prepareTools()
|
||||||
await withDatabase(mongodbUri, async (db, client) => {
|
await withDatabase(mongodbUri, async (db, client) => {
|
||||||
await withStorage(mongodbUri, async (adapter) => {
|
await withStorage(mongodbUri, async (adapter) => {
|
||||||
@ -1063,7 +1064,7 @@ export function devTool (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const wsId = getWorkspaceId(workspace.workspace, productId)
|
const wsId = getWorkspaceId(workspace.workspace, productId)
|
||||||
await moveFiles(toolCtx, wsId, exAdapter)
|
await moveFiles(toolCtx, wsId, exAdapter, parseInt(cmd.blobLimit))
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -13,14 +13,15 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { type MeasureContext, type WorkspaceId } from '@hcengineering/core'
|
import { type Blob, type MeasureContext, type WorkspaceId } from '@hcengineering/core'
|
||||||
import { type StorageAdapterEx } from '@hcengineering/server-core'
|
import { type StorageAdapterEx } from '@hcengineering/server-core'
|
||||||
import { PassThrough } from 'stream'
|
import { PassThrough } from 'stream'
|
||||||
|
|
||||||
export async function moveFiles (
|
export async function moveFiles (
|
||||||
ctx: MeasureContext,
|
ctx: MeasureContext,
|
||||||
workspaceId: WorkspaceId,
|
workspaceId: WorkspaceId,
|
||||||
exAdapter: StorageAdapterEx
|
exAdapter: StorageAdapterEx,
|
||||||
|
blobSizeLimitMb: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (exAdapter.adapters === undefined) return
|
if (exAdapter.adapters === undefined) return
|
||||||
|
|
||||||
@ -45,13 +46,20 @@ export async function moveFiles (
|
|||||||
if (blob === undefined) continue
|
if (blob === undefined) continue
|
||||||
if (blob.provider === target) continue
|
if (blob.provider === target) continue
|
||||||
|
|
||||||
|
if (blob.size > blobSizeLimitMb * 1024 * 1024) {
|
||||||
|
console.log('skipping large blob', name, data._id, Math.round(blob.size / 1024 / 1024))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const readable = await exAdapter.get(ctx, workspaceId, data._id)
|
await retryOnFailure(
|
||||||
readable.on('end', () => {
|
ctx,
|
||||||
readable.destroy()
|
5,
|
||||||
})
|
async () => {
|
||||||
const stream = readable.pipe(new PassThrough())
|
await moveFile(ctx, exAdapter, workspaceId, blob)
|
||||||
await exAdapter.put(ctx, workspaceId, data._id, stream, blob.contentType, blob.size)
|
},
|
||||||
|
50
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('failed to process blob', name, data._id, err)
|
console.error('failed to process blob', name, data._id, err)
|
||||||
}
|
}
|
||||||
@ -66,3 +74,44 @@ export async function moveFiles (
|
|||||||
|
|
||||||
console.log('...done', workspaceId.name, count)
|
console.log('...done', workspaceId.name, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function moveFile (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
exAdapter: StorageAdapterEx,
|
||||||
|
workspaceId: WorkspaceId,
|
||||||
|
blob: Blob
|
||||||
|
): Promise<void> {
|
||||||
|
const readable = await exAdapter.get(ctx, workspaceId, blob._id)
|
||||||
|
try {
|
||||||
|
readable.on('end', () => {
|
||||||
|
readable.destroy()
|
||||||
|
})
|
||||||
|
const stream = readable.pipe(new PassThrough())
|
||||||
|
await exAdapter.put(ctx, workspaceId, blob._id, stream, blob.contentType, blob.size)
|
||||||
|
} catch (err) {
|
||||||
|
readable.destroy()
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function retryOnFailure<T> (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
retries: number,
|
||||||
|
op: () => Promise<T>,
|
||||||
|
delay: number = 0
|
||||||
|
): Promise<T> {
|
||||||
|
let error: any
|
||||||
|
while (retries > 0) {
|
||||||
|
retries--
|
||||||
|
try {
|
||||||
|
return await op()
|
||||||
|
} catch (err: any) {
|
||||||
|
error = err
|
||||||
|
ctx.error('error', { err, retries })
|
||||||
|
if (retries !== 0 && delay > 0) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delay))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user