UBERF-5061 Rewrite queries in index consistency check (#4430)

Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2024-01-25 14:25:42 +07:00 committed by GitHub
parent 136134de60
commit 3ee98b67ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 30 deletions

View File

@ -318,6 +318,11 @@ export class TDocIndexState extends TDoc implements DocIndexState {
@Index(IndexKind.Indexed) @Index(IndexKind.Indexed)
@Hidden() @Hidden()
stages!: Record<string, boolean | string> stages!: Record<string, boolean | string>
@Prop(TypeString(), getEmbeddedLabel('Generation'))
@Index(IndexKind.Indexed)
@Hidden()
generationId?: string
} }
@Model(core.class.IndexStageState, core.class.Doc, DOMAIN_DOC_INDEX_STATE) @Model(core.class.IndexStageState, core.class.Doc, DOMAIN_DOC_INDEX_STATE)

View File

@ -432,6 +432,8 @@ export interface DocIndexState extends Doc {
attachedTo?: Ref<Doc> attachedTo?: Ref<Doc>
attachedToClass?: Ref<Class<Doc>> attachedToClass?: Ref<Class<Doc>>
generationId?: string
// States for stages // States for stages
stages: Record<string, boolean | string> stages: Record<string, boolean | string>

View File

@ -17,6 +17,7 @@ import core, {
AttachedDoc, AttachedDoc,
Class, Class,
DOMAIN_DOC_INDEX_STATE, DOMAIN_DOC_INDEX_STATE,
DOMAIN_FULLTEXT_BLOB,
Doc, Doc,
DocIndexState, DocIndexState,
DocumentQuery, DocumentQuery,
@ -32,7 +33,8 @@ import core, {
setObjectValue, setObjectValue,
toFindResult, toFindResult,
versionToString, versionToString,
docKey docKey,
generateId
} from '@hcengineering/core' } from '@hcengineering/core'
import { DbAdapter } from '../adapter' import { DbAdapter } from '../adapter'
import { RateLimitter } from '../limitter' import { RateLimitter } from '../limitter'
@ -600,44 +602,83 @@ export class FullTextIndexPipeline implements FullTextPipeline {
console.log(this.workspace.name, 'checking index', c) console.log(this.workspace.name, 'checking index', c)
// All saved state documents const generationId = generateId()
const states = (
await this.storage.findAll(core.class.DocIndexState, { objectClass: c }, { projection: { _id: 1 } }) let lastId = ''
).map((it) => it._id)
while (true) { while (true) {
if (this.cancelling) { if (this.cancelling) {
return return
} }
let newDocs: DocIndexState[] = [] let newDocs: DocIndexState[] = []
let updates = new Map<Ref<DocIndexState>, DocumentUpdate<DocIndexState>>()
try { try {
newDocs = ( const docs = await dbStorage.findAll<Doc>(
await dbStorage.findAll<Doc>( this.metrics,
this.metrics, c,
c, { _class: c, _id: { $gt: lastId as any } },
{ _class: c, _id: { $nin: states } }, {
{ limit: 500, projection: { _id: 1, attachedTo: 1, attachedToClass: 1 } as any } limit: 10000,
sort: { _id: 1 },
projection: { _id: 1, attachedTo: 1, attachedToClass: 1 } as any
}
)
if (docs.length === 0) {
// All updated for this class
break
}
lastId = docs[docs.length - 1]._id
const states = (
await this.storage.findAll(
core.class.DocIndexState,
{
objectClass: c,
_id: {
$gte: docs[0]._id as any,
$lte: docs[docs.length - 1]._id as any
}
},
{ projection: { _id: 1 } }
) )
).map((it) => { ).map((it) => it._id)
return createStateDoc(it._id, c, { const statesSet = new Set(states)
stages: {},
attributes: {}, // create missing index states
removed: false, newDocs = docs
space: it.space, .filter((it) => !statesSet.has(it._id as Ref<DocIndexState>))
attachedTo: (it as AttachedDoc)?.attachedTo ?? undefined, .map((it) => {
attachedToClass: (it as AttachedDoc)?.attachedToClass ?? undefined return createStateDoc(it._id, c, {
generationId,
stages: {},
attributes: {},
removed: false,
space: it.space,
attachedTo: (it as AttachedDoc)?.attachedTo ?? undefined,
attachedToClass: (it as AttachedDoc)?.attachedToClass ?? undefined
})
})
// update generationId for existing index states
updates = new Map()
docs
.filter((it) => statesSet.has(it._id as Ref<DocIndexState>))
.forEach((it) => {
updates.set(it._id as Ref<DocIndexState>, { generationId })
}) })
})
} catch (e) { } catch (e) {
console.error(e) console.error(e)
break break
} }
states.push(...newDocs.map((it) => it._id)) try {
await this.storage.update(DOMAIN_DOC_INDEX_STATE, updates)
if (newDocs.length === 0) { } catch (err: any) {
// All updated for this class console.error(err)
break
} }
try { try {
@ -646,11 +687,20 @@ export class FullTextIndexPipeline implements FullTextPipeline {
console.error(err) console.error(err)
} }
} }
const statesSet = new Set(states)
const docIds = (await dbStorage.findAll<Doc>(this.metrics, c, { _class: c }, { projection: { _id: 1 } })) // remove index states for documents that do not exist
.filter((it) => !statesSet.has(it._id as Ref<DocIndexState>)) const toRemove = (
.map((it) => it._id) await this.storage.findAll(
await this.storage.clean(DOMAIN_DOC_INDEX_STATE, docIds) core.class.DocIndexState,
{ objectClass: c, generationId: { $ne: generationId } },
{ projection: { _id: 1 } }
)
).map((it) => it._id)
if (toRemove.length > 0) {
await this.storage.clean(DOMAIN_DOC_INDEX_STATE, toRemove)
await this.storage.clean(DOMAIN_FULLTEXT_BLOB, toRemove)
}
} }
// Clean for non existing classes // Clean for non existing classes