mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-30 12:15:51 +00:00
Fix backup all and disable full check for migration
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
059cab66fb
commit
622844c42f
@ -25,7 +25,15 @@ import accountPlugin, {
|
|||||||
type AccountDB
|
type AccountDB
|
||||||
} from '@hcengineering/account'
|
} from '@hcengineering/account'
|
||||||
import { setMetadata } from '@hcengineering/platform'
|
import { setMetadata } from '@hcengineering/platform'
|
||||||
import { backup, createFileBackupStorage, createStorageBackupStorage, restore } from '@hcengineering/server-backup'
|
import {
|
||||||
|
backup,
|
||||||
|
backupFind,
|
||||||
|
checkBackupIntegrity,
|
||||||
|
compactBackup,
|
||||||
|
createFileBackupStorage,
|
||||||
|
createStorageBackupStorage,
|
||||||
|
restore
|
||||||
|
} from '@hcengineering/server-backup'
|
||||||
import serverClientPlugin, { getAccountClient } from '@hcengineering/server-client'
|
import serverClientPlugin, { getAccountClient } from '@hcengineering/server-client'
|
||||||
import {
|
import {
|
||||||
registerAdapterFactory,
|
registerAdapterFactory,
|
||||||
@ -36,7 +44,7 @@ import {
|
|||||||
setAdapterSecurity,
|
setAdapterSecurity,
|
||||||
sharedPipelineContextVars
|
sharedPipelineContextVars
|
||||||
} from '@hcengineering/server-pipeline'
|
} from '@hcengineering/server-pipeline'
|
||||||
import serverToken from '@hcengineering/server-token'
|
import serverToken, { generateToken } from '@hcengineering/server-token'
|
||||||
import { createWorkspace, upgradeWorkspace } from '@hcengineering/workspace-service'
|
import { createWorkspace, upgradeWorkspace } from '@hcengineering/workspace-service'
|
||||||
|
|
||||||
import { buildStorageFromConfig, createStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage'
|
import { buildStorageFromConfig, createStorageFromConfig, storageConfigFromEnv } from '@hcengineering/server-storage'
|
||||||
@ -47,12 +55,15 @@ import {
|
|||||||
AccountRole,
|
AccountRole,
|
||||||
MeasureMetricsContext,
|
MeasureMetricsContext,
|
||||||
metricsToString,
|
metricsToString,
|
||||||
type PersonId,
|
systemAccountUuid,
|
||||||
type WorkspaceUuid,
|
|
||||||
type Data,
|
type Data,
|
||||||
|
type Doc,
|
||||||
|
type PersonId,
|
||||||
|
type Ref,
|
||||||
type Tx,
|
type Tx,
|
||||||
type Version,
|
type Version,
|
||||||
type WorkspaceDataId
|
type WorkspaceDataId,
|
||||||
|
type WorkspaceUuid
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model'
|
import { consoleModelLogger, type MigrateOperation } from '@hcengineering/model'
|
||||||
import {
|
import {
|
||||||
@ -76,8 +87,8 @@ import { getAccountDBUrl, getMongoDBUrl } from './__start'
|
|||||||
import { changeConfiguration } from './configuration'
|
import { changeConfiguration } from './configuration'
|
||||||
import { reindexWorkspace } from './fulltext'
|
import { reindexWorkspace } from './fulltext'
|
||||||
|
|
||||||
import { getToolToken, getWorkspace, getWorkspaceTransactorEndpoint } from './utils'
|
|
||||||
import { moveAccountDbFromMongoToPG } from './db'
|
import { moveAccountDbFromMongoToPG } from './db'
|
||||||
|
import { getToolToken, getWorkspace, getWorkspaceTransactorEndpoint } from './utils'
|
||||||
|
|
||||||
const colorConstants = {
|
const colorConstants = {
|
||||||
colorRed: '\u001b[31m',
|
colorRed: '\u001b[31m',
|
||||||
@ -958,30 +969,30 @@ export function devTool (
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// program
|
program
|
||||||
// .command('backup-find <dirName> <fileId>')
|
.command('backup-find <dirName> <fileId>')
|
||||||
// .description('dump workspace transactions and minio resources')
|
.description('dump workspace transactions and minio resources')
|
||||||
// .option('-d, --domain <domain>', 'Check only domain')
|
.option('-d, --domain <domain>', 'Check only domain')
|
||||||
// .action(async (dirName: string, fileId: string, cmd: { domain: string | undefined }) => {
|
.action(async (dirName: string, fileId: string, cmd: { domain: string | undefined }) => {
|
||||||
// const storage = await createFileBackupStorage(dirName)
|
const storage = await createFileBackupStorage(dirName)
|
||||||
// await backupFind(storage, fileId as unknown as Ref<Doc>, cmd.domain)
|
await backupFind(storage, fileId as unknown as Ref<Doc>, cmd.domain)
|
||||||
// })
|
})
|
||||||
|
|
||||||
// program
|
program
|
||||||
// .command('backup-compact <dirName>')
|
.command('backup-compact <dirName>')
|
||||||
// .description('Compact a given backup, will create one snapshot clean unused resources')
|
.description('Compact a given backup, will create one snapshot clean unused resources')
|
||||||
// .option('-f, --force', 'Force compact.', false)
|
.option('-f, --force', 'Force compact.', false)
|
||||||
// .action(async (dirName: string, cmd: { force: boolean }) => {
|
.action(async (dirName: string, cmd: { force: boolean }) => {
|
||||||
// const storage = await createFileBackupStorage(dirName)
|
const storage = await createFileBackupStorage(dirName)
|
||||||
// await compactBackup(toolCtx, storage, cmd.force)
|
await compactBackup(toolCtx, storage, cmd.force)
|
||||||
// })
|
})
|
||||||
// program
|
program
|
||||||
// .command('backup-check <dirName>')
|
.command('backup-check <dirName>')
|
||||||
// .description('Compact a given backup, will create one snapshot clean unused resources')
|
.description('Compact a given backup, will create one snapshot clean unused resources')
|
||||||
// .action(async (dirName: string, cmd: any) => {
|
.action(async (dirName: string, cmd: any) => {
|
||||||
// const storage = await createFileBackupStorage(dirName)
|
const storage = await createFileBackupStorage(dirName)
|
||||||
// await checkBackupIntegrity(toolCtx, storage)
|
await checkBackupIntegrity(toolCtx, storage)
|
||||||
// })
|
})
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('backup-check-all')
|
.command('backup-check-all')
|
||||||
@ -999,23 +1010,23 @@ export function devTool (
|
|||||||
|
|
||||||
const skipWorkspaces = new Set(cmd.skip.split(',').map((it) => it.trim()))
|
const skipWorkspaces = new Set(cmd.skip.split(',').map((it) => it.trim()))
|
||||||
|
|
||||||
const token = generateToken(systemAccountEmail, getWorkspaceId(''))
|
const token = generateToken(systemAccountUuid, '' as WorkspaceUuid)
|
||||||
const workspaces = (await listAccountWorkspaces(token, cmd.region))
|
const workspaces = (await getAccountClient(token).listWorkspaces(cmd.region))
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const bsize = b.backupInfo?.backupSize ?? 0
|
const bsize = b.backupInfo?.backupSize ?? 0
|
||||||
const asize = a.backupInfo?.backupSize ?? 0
|
const asize = a.backupInfo?.backupSize ?? 0
|
||||||
return bsize - asize
|
return bsize - asize
|
||||||
})
|
})
|
||||||
.filter((it) => (cmd.workspace === '' || cmd.workspace === it.workspace) && !skipWorkspaces.has(it.workspace))
|
.filter((it) => (cmd.workspace === '' || cmd.workspace === it.url) && !skipWorkspaces.has(it.url))
|
||||||
|
|
||||||
const backupStorageConfig = storageConfigFromEnv(process.env.STORAGE)
|
const backupStorageConfig = storageConfigFromEnv(process.env.STORAGE)
|
||||||
const storageAdapter = createStorageFromConfig(backupStorageConfig.storages[0])
|
const storageAdapter = createStorageFromConfig(backupStorageConfig.storages[0])
|
||||||
for (const ws of workspaces) {
|
for (const ws of workspaces) {
|
||||||
const lastVisitDays = Math.floor((Date.now() - ws.lastVisit) / 1000 / 3600 / 24)
|
const lastVisitDays = Math.floor((Date.now() - (ws.lastVisit ?? 0)) / 1000 / 3600 / 24)
|
||||||
|
|
||||||
toolCtx.warn('--- checking workspace backup', {
|
toolCtx.warn('--- checking workspace backup', {
|
||||||
url: ws.workspaceUrl,
|
url: ws.url,
|
||||||
id: ws.workspace,
|
id: ws.uuid,
|
||||||
lastVisitDays,
|
lastVisitDays,
|
||||||
backupSize: ws.backupInfo?.blobsSize ?? 0,
|
backupSize: ws.backupInfo?.blobsSize ?? 0,
|
||||||
mode: ws.mode
|
mode: ws.mode
|
||||||
@ -1030,8 +1041,12 @@ export function devTool (
|
|||||||
const storage = await createStorageBackupStorage(
|
const storage = await createStorageBackupStorage(
|
||||||
toolCtx,
|
toolCtx,
|
||||||
storageAdapter,
|
storageAdapter,
|
||||||
getWorkspaceId(bucketName),
|
{
|
||||||
ws.workspace
|
uuid: 'backup' as WorkspaceUuid,
|
||||||
|
url: 'backup',
|
||||||
|
dataId: bucketName as WorkspaceDataId
|
||||||
|
},
|
||||||
|
ws.dataId ?? ws.uuid
|
||||||
)
|
)
|
||||||
await checkBackupIntegrity(toolCtx, storage)
|
await checkBackupIntegrity(toolCtx, storage)
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@ -1042,7 +1057,7 @@ export function devTool (
|
|||||||
time: ed - st
|
time: ed - st
|
||||||
})
|
})
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
toolCtx.error('REstore of f workspace failedarchive workspace', { workspace: ws.workspace })
|
toolCtx.error('Restore of f workspace failedarchive workspace', { workspace: ws.url })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await storageAdapter.close()
|
await storageAdapter.close()
|
||||||
|
@ -16,15 +16,16 @@
|
|||||||
|
|
||||||
import core, {
|
import core, {
|
||||||
DOMAIN_TX,
|
DOMAIN_TX,
|
||||||
getWorkspaceId,
|
|
||||||
type BackupClient,
|
type BackupClient,
|
||||||
type BaseWorkspaceInfo,
|
|
||||||
type Class,
|
type Class,
|
||||||
type Client as CoreClient,
|
type Client as CoreClient,
|
||||||
type Doc,
|
type Doc,
|
||||||
type MeasureContext,
|
type MeasureContext,
|
||||||
type Ref,
|
type Ref,
|
||||||
type Tx,
|
type Tx,
|
||||||
|
type WorkspaceDataId,
|
||||||
|
type WorkspaceIds,
|
||||||
|
type WorkspaceInfoWithStatus,
|
||||||
type WorkspaceUuid
|
type WorkspaceUuid
|
||||||
} from '@hcengineering/core'
|
} from '@hcengineering/core'
|
||||||
import { getMongoClient, getWorkspaceMongoDB } from '@hcengineering/mongo'
|
import { getMongoClient, getWorkspaceMongoDB } from '@hcengineering/mongo'
|
||||||
@ -120,7 +121,7 @@ export async function backupRestore (
|
|||||||
ctx: MeasureContext,
|
ctx: MeasureContext,
|
||||||
dbURL: string,
|
dbURL: string,
|
||||||
bucketName: string,
|
bucketName: string,
|
||||||
workspace: BaseWorkspaceInfo,
|
workspace: WorkspaceInfoWithStatus,
|
||||||
pipelineFactoryFactory: (mongoUrl: string, storage: StorageAdapter) => PipelineFactory,
|
pipelineFactoryFactory: (mongoUrl: string, storage: StorageAdapter) => PipelineFactory,
|
||||||
skipDomains: string[]
|
skipDomains: string[]
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
@ -144,17 +145,20 @@ export async function backupRestore (
|
|||||||
const storage = await createStorageBackupStorage(
|
const storage = await createStorageBackupStorage(
|
||||||
ctx,
|
ctx,
|
||||||
storageAdapter,
|
storageAdapter,
|
||||||
getWorkspaceId(bucketName),
|
{
|
||||||
workspace.workspace
|
uuid: 'backup' as WorkspaceUuid,
|
||||||
|
url: bucketName,
|
||||||
|
dataId: bucketName as WorkspaceDataId
|
||||||
|
},
|
||||||
|
workspace.dataId ?? workspace.uuid
|
||||||
)
|
)
|
||||||
const wsUrl: WorkspaceIdWithUrl = {
|
const wsUrl: WorkspaceIds = {
|
||||||
name: workspace.workspace,
|
|
||||||
uuid: workspace.uuid,
|
uuid: workspace.uuid,
|
||||||
workspaceName: workspace.workspaceName ?? '',
|
dataId: workspace.dataId,
|
||||||
workspaceUrl: workspace.workspaceUrl ?? ''
|
url: workspace.url
|
||||||
}
|
}
|
||||||
const result: boolean = await ctx.with('restore', { workspace: workspace.workspace }, (ctx) =>
|
const result: boolean = await ctx.with('restore', { workspace: workspace.url }, (ctx) =>
|
||||||
restore(ctx, '', getWorkspaceId(workspace.workspace), storage, {
|
restore(ctx, '', wsUrl, storage, {
|
||||||
date: -1,
|
date: -1,
|
||||||
skip: new Set(skipDomains),
|
skip: new Set(skipDomains),
|
||||||
recheck: false,
|
recheck: false,
|
||||||
|
@ -2062,7 +2062,7 @@ export async function restore (
|
|||||||
if (docsToAdd.size === 0) {
|
if (docsToAdd.size === 0) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
ctx.info('processing', { storageFile: sf, processed, workspace: workspaceId.name })
|
ctx.info('processing', { storageFile: sf, processed, workspace: wsIds.url })
|
||||||
try {
|
try {
|
||||||
const readStream = await storage.load(sf)
|
const readStream = await storage.load(sf)
|
||||||
const ex = extract()
|
const ex = extract()
|
||||||
@ -2107,7 +2107,7 @@ export async function restore (
|
|||||||
try {
|
try {
|
||||||
doc = JSON.parse(bf.toString()) as Doc
|
doc = JSON.parse(bf.toString()) as Doc
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ctx.warn('failed to parse blob metadata', { name, workspace: workspaceId.name, err })
|
ctx.warn('failed to parse blob metadata', { name, workspace: wsIds.url, err })
|
||||||
next()
|
next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2165,7 +2165,7 @@ export async function restore (
|
|||||||
|
|
||||||
await endPromise
|
await endPromise
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
ctx.error('failed to processing', { storageFile: sf, processed, workspace: workspaceId.name })
|
ctx.error('failed to processing', { storageFile: sf, processed, workspace: wsIds.url })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,7 +479,7 @@ export class WorkspaceWorker {
|
|||||||
await sendEvent('archiving-backup-started', 0)
|
await sendEvent('archiving-backup-started', 0)
|
||||||
|
|
||||||
await this.sendTransactorMaitenance(token, workspace.uuid)
|
await this.sendTransactorMaitenance(token, workspace.uuid)
|
||||||
if (await this.doBackup(ctx, workspace, opt)) {
|
if (await this.doBackup(ctx, workspace, opt, true)) {
|
||||||
await sendEvent('archiving-backup-done', 100)
|
await sendEvent('archiving-backup-done', 100)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -516,7 +516,7 @@ export class WorkspaceWorker {
|
|||||||
case 'migration-backup':
|
case 'migration-backup':
|
||||||
await sendEvent('migrate-backup-started', 0)
|
await sendEvent('migrate-backup-started', 0)
|
||||||
await this.sendTransactorMaitenance(token, workspace.uuid)
|
await this.sendTransactorMaitenance(token, workspace.uuid)
|
||||||
if (await this.doBackup(ctx, workspace, opt)) {
|
if (await this.doBackup(ctx, workspace, opt, false)) {
|
||||||
await sendEvent('migrate-backup-done', 100)
|
await sendEvent('migrate-backup-done', 100)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -551,7 +551,12 @@ export class WorkspaceWorker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async doBackup (ctx: MeasureContext, workspace: WorkspaceInfoWithStatus, opt: WorkspaceOptions): Promise<boolean> {
|
private async doBackup (
|
||||||
|
ctx: MeasureContext,
|
||||||
|
workspace: WorkspaceInfoWithStatus,
|
||||||
|
opt: WorkspaceOptions,
|
||||||
|
doFullCheck: boolean
|
||||||
|
): Promise<boolean> {
|
||||||
if (opt.backup === undefined) {
|
if (opt.backup === undefined) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -619,7 +624,7 @@ export class WorkspaceWorker {
|
|||||||
50000,
|
50000,
|
||||||
['blob'],
|
['blob'],
|
||||||
sharedPipelineContextVars,
|
sharedPipelineContextVars,
|
||||||
true, // Do a full check
|
doFullCheck, // Do full check based on config, do not do for migration, it is to slow, will perform before migration.
|
||||||
(_p: number) => {
|
(_p: number) => {
|
||||||
if (progress !== Math.round(_p)) {
|
if (progress !== Math.round(_p)) {
|
||||||
progress = Math.round(_p)
|
progress = Math.round(_p)
|
||||||
|
Loading…
Reference in New Issue
Block a user