UBERF-8461: Fix migration do not clean backup info (#6913)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2024-10-14 22:22:19 +07:00 committed by Andrey Sobolev
parent 00783a19dd
commit 3ec53392b0
No known key found for this signature in database
GPG Key ID: BD80F68D68D8F7F2
13 changed files with 118 additions and 41 deletions

View File

@ -14,13 +14,13 @@
// //
import { getAccountDB, listWorkspacesRaw } from '@hcengineering/account' import { getAccountDB, listWorkspacesRaw } from '@hcengineering/account'
import attachment from '@hcengineering/attachment'
import calendar from '@hcengineering/calendar' import calendar from '@hcengineering/calendar'
import chunter, { type ChatMessage } from '@hcengineering/chunter' import chunter, { type ChatMessage } from '@hcengineering/chunter'
import { loadCollaborativeDoc, saveCollaborativeDoc, yDocToBuffer } from '@hcengineering/collaboration' import { loadCollaborativeDoc, saveCollaborativeDoc, yDocToBuffer } from '@hcengineering/collaboration'
import contact from '@hcengineering/contact' import contact from '@hcengineering/contact'
import core, { import core, {
type ArrOf, type ArrOf,
type AttachedDoc,
type BackupClient, type BackupClient,
type Class, type Class,
ClassifierKind, ClassifierKind,
@ -45,11 +45,14 @@ import core, {
SortingOrder, SortingOrder,
type Status, type Status,
type StatusCategory, type StatusCategory,
type Tx,
type TxCUD, type TxCUD,
type TxCollectionCUD,
type TxCreateDoc, type TxCreateDoc,
type TxMixin, type TxMixin,
TxOperations, TxOperations,
TxProcessor, TxProcessor,
type TxRemoveDoc,
type TxUpdateDoc, type TxUpdateDoc,
type WorkspaceId, type WorkspaceId,
generateId, generateId,
@ -93,30 +96,6 @@ export async function cleanWorkspace (
const hierarchy = ops.getHierarchy() const hierarchy = ops.getHierarchy()
const attachments = await ops.findAll(attachment.class.Attachment, {})
const contacts = await ops.findAll(contact.class.Contact, {})
const files = new Set(
attachments.map((it) => it.file as string).concat(contacts.map((it) => it.avatar).filter((it) => it) as string[])
)
const minioList = await storageAdapter.listStream(ctx, workspaceId)
const toClean: string[] = []
while (true) {
const mvFiles = await minioList.next()
if (mvFiles.length === 0) {
break
}
for (const mv of mvFiles) {
if (!files.has(mv._id)) {
toClean.push(mv._id)
}
}
}
await storageAdapter.remove(ctx, workspaceId, toClean)
if (opt.recruit) { if (opt.recruit) {
const contacts = await ops.findAll(recruit.mixin.Candidate, {}) const contacts = await ops.findAll(recruit.mixin.Candidate, {})
console.log('removing Talents', contacts.length) console.log('removing Talents', contacts.length)
@ -134,13 +113,6 @@ export async function cleanWorkspace (
const t2 = Date.now() const t2 = Date.now()
console.log('remove time:', t2 - t, filter.length) console.log('remove time:', t2 - t, filter.length)
} }
// const vacancies = await ops.findAll(recruit.class.Vacancy, {})
// console.log('removing vacancies', vacancies.length)
// for (const c of vacancies) {
// console.log('Remove', c.name)
// await ops.remove(c)
// }
} }
if (opt.tracker) { if (opt.tracker) {
@ -166,12 +138,39 @@ export async function cleanWorkspace (
const db = getWorkspaceMongoDB(_client, workspaceId) const db = getWorkspaceMongoDB(_client, workspaceId)
if (opt.removedTx) { if (opt.removedTx) {
const txes = await db.collection(DOMAIN_TX).find({}).toArray() let processed = 0
const iterator = db.collection(DOMAIN_TX).find({})
while (true) {
const txes: Tx[] = []
for (const tx of txes) { const doc = await iterator.next()
if (tx._class === core.class.TxRemoveDoc) { if (doc == null) {
// We need to remove all update and create operations for document break
await db.collection(DOMAIN_TX).deleteMany({ objectId: tx.objectId }) }
txes.push(doc as unknown as Tx)
if (iterator.bufferedCount() > 0) {
txes.push(...(iterator.readBufferedDocuments() as unknown as Tx[]))
}
for (const tx of txes) {
if (tx._class === core.class.TxRemoveDoc) {
// We need to remove all update and create operations for document
await db.collection(DOMAIN_TX).deleteMany({ objectId: (tx as TxRemoveDoc<Doc>).objectId })
processed++
}
if (
tx._class === core.class.TxCollectionCUD &&
(tx as TxCollectionCUD<Doc, AttachedDoc>).tx._class === core.class.TxRemoveDoc
) {
// We need to remove all update and create operations for document
await db.collection(DOMAIN_TX).deleteMany({
'tx.objectId': ((tx as TxCollectionCUD<Doc, AttachedDoc>).tx as TxRemoveDoc<Doc>).objectId
})
processed++
}
}
if (processed % 1000 === 0) {
console.log('processed', processed)
} }
} }
} }

View File

@ -221,6 +221,12 @@ export const activityOperation: MigrateOperation = {
{ {
state: 'migrate-activity-markup', state: 'migrate-activity-markup',
func: migrateActivityMarkup func: migrateActivityMarkup
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_ACTIVITY, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -13,9 +13,24 @@
// limitations under the License. // limitations under the License.
// //
import { type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' import {
tryMigrate,
type MigrateOperation,
type MigrationClient,
type MigrationUpgradeClient
} from '@hcengineering/model'
import { attachmentId, DOMAIN_ATTACHMENT } from '.'
export const attachmentOperation: MigrateOperation = { export const attachmentOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {}, async migrate (client: MigrationClient): Promise<void> {
await tryMigrate(client, attachmentId, [
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_ATTACHMENT, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
}
])
},
async upgrade (state: Map<string, Set<string>>, client: () => Promise<MigrationUpgradeClient>): Promise<void> {} async upgrade (state: Map<string, Set<string>>, client: () => Promise<MigrationUpgradeClient>): Promise<void> {}
} }

View File

@ -362,6 +362,12 @@ export const chunterOperation: MigrateOperation = {
'attributeUpdates.attrKey': 'members' 'attributeUpdates.attrKey': 'members'
}) })
} }
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_CHUNTER, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -1,5 +1,6 @@
// //
import { AvatarType, type Contact, type Person, type PersonSpace } from '@hcengineering/contact'
import { import {
type Class, type Class,
type Doc, type Doc,
@ -24,9 +25,8 @@ import {
import activity, { DOMAIN_ACTIVITY } from '@hcengineering/model-activity' import activity, { DOMAIN_ACTIVITY } from '@hcengineering/model-activity'
import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import core, { DOMAIN_SPACE } from '@hcengineering/model-core'
import { DOMAIN_VIEW } from '@hcengineering/model-view' import { DOMAIN_VIEW } from '@hcengineering/model-view'
import { AvatarType, type Contact, type Person, type PersonSpace } from '@hcengineering/contact'
import contact, { contactId, DOMAIN_CONTACT } from './index' import contact, { contactId, DOMAIN_CHANNEL, DOMAIN_CONTACT } from './index'
async function createEmployeeEmail (client: TxOperations): Promise<void> { async function createEmployeeEmail (client: TxOperations): Promise<void> {
const employees = await client.findAll(contact.mixin.Employee, {}) const employees = await client.findAll(contact.mixin.Employee, {})
@ -300,6 +300,13 @@ export const contactOperation: MigrateOperation = {
{ {
state: 'create-person-spaces-v1', state: 'create-person-spaces-v1',
func: createPersonSpaces func: createPersonSpaces
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_CONTACT, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
await client.update(DOMAIN_CHANNEL, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -301,6 +301,13 @@ export const coreOperation: MigrateOperation = {
{ {
state: 'collaborative-content-to-storage', state: 'collaborative-content-to-storage',
func: migrateCollaborativeContentToStorage func: migrateCollaborativeContentToStorage
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_TX, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
await client.update(DOMAIN_SPACE, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -228,6 +228,12 @@ export const documentOperation: MigrateOperation = {
{ {
state: 'renameFields', state: 'renameFields',
func: renameFields func: renameFields
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_DOCUMENT, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -132,6 +132,12 @@ export const driveOperation: MigrateOperation = {
{ {
state: 'renameFields', state: 'renameFields',
func: renameFields func: renameFields
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_DRIVE, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -393,6 +393,12 @@ export const notificationOperation: MigrateOperation = {
{ hidden: false } { hidden: false }
) )
} }
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_DOC_NOTIFY, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])

View File

@ -584,6 +584,12 @@ export const taskOperation: MigrateOperation = {
func: async (client: MigrationClient) => { func: async (client: MigrationClient) => {
await migrateSpace(client, task.space.Sequence, core.space.Workspace, [DOMAIN_KANBAN]) await migrateSpace(client, task.space.Sequence, core.space.Workspace, [DOMAIN_KANBAN])
} }
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_TASK, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -172,6 +172,12 @@ export const timeOperation: MigrateOperation = {
func: async (client) => { func: async (client) => {
await fillProps(client) await fillProps(client)
} }
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_TIME, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -86,6 +86,12 @@ export const viewOperation: MigrateOperation = {
{ {
state: 'remove-done-state-filter', state: 'remove-done-state-filter',
func: removeDoneStateFilter func: removeDoneStateFilter
},
{
state: 'fix-rename-backups',
func: async (client: MigrationClient): Promise<void> => {
await client.update(DOMAIN_VIEW, { '%hash%': { $exists: true } }, { $set: { '%hash%': null } })
}
} }
]) ])
}, },

View File

@ -251,6 +251,7 @@ abstract class MongoAdapterBase implements DbAdapter {
operations: DocumentUpdate<T> operations: DocumentUpdate<T>
): Promise<void> { ): Promise<void> {
if (isOperator(operations)) { if (isOperator(operations)) {
await this.db.collection(domain).updateMany(this.translateRawQuery(query), { $set: { '%hash%': null } })
await this.db await this.db
.collection(domain) .collection(domain)
.updateMany(this.translateRawQuery(query), { ...operations } as unknown as UpdateFilter<Document>) .updateMany(this.translateRawQuery(query), { ...operations } as unknown as UpdateFilter<Document>)