Skip to content

Commit 0915830

Browse files
committed
save
1 parent 5403ef5 commit 0915830

File tree

6 files changed

+94
-142
lines changed

6 files changed

+94
-142
lines changed

typeorm/typeorm-store/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"dependencies": {
2121
"@subsquid/typeorm-config": "^4.1.1",
2222
"@subsquid/util-internal": "^3.2.0",
23-
"@subsquid/logger": "~1.3.3"
23+
"@subsquid/logger": "^1.3.3"
2424
},
2525
"peerDependencies": {
2626
"typeorm": "^0.3.17",

typeorm/typeorm-store/src/database.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export class TypeormDatabase {
249249
await store.flush()
250250
if (this.resetMode === 'BATCH') store.reset()
251251
} finally {
252-
store._close()
252+
store['isClosed'] = true
253253
}
254254
}
255255

typeorm/typeorm-store/src/store.ts

Lines changed: 65 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import {EntityManager, EntityMetadata, FindOptionsOrder, FindOptionsRelations, FindOptionsWhere} from 'typeorm'
1+
import {
2+
EntityManager,
3+
EntityMetadata,
4+
EntityNotFoundError,
5+
FindOptionsOrder,
6+
FindOptionsRelations,
7+
FindOptionsWhere,
8+
} from 'typeorm'
29
import {EntityTarget} from 'typeorm/common/EntityTarget'
310
import {ChangeWriter} from './utils/changeWriter'
411
import {StateManager} from './utils/stateManager'
@@ -237,7 +244,7 @@ export class Store {
237244
private async _delete(metadata: EntityMetadata, ids: string[]) {
238245
this.logger?.debug(`delete ${metadata.name} ${ids.length} entities`)
239246
await this.changes?.writeDelete(metadata, ids)
240-
await this.em.delete(metadata.target, ids) // TODO: should be split by chunks too?
247+
await this.em.delete(metadata.target, ids) // NOTE: should be split by chunks too?
241248
}
242249

243250
async count<E extends EntityLiteral>(target: EntityTarget<E>, options?: FindManyOptions<E>): Promise<number> {
@@ -250,16 +257,19 @@ export class Store {
250257
target: EntityTarget<E>,
251258
where: FindOptionsWhere<E> | FindOptionsWhere<E>[]
252259
): Promise<number> {
253-
return await this.performRead(async () => {
254-
return await this.em.countBy(target, where)
255-
})
260+
return await this.count(target, {where})
256261
}
257262

258263
async find<E extends EntityLiteral>(target: EntityTarget<E>, options: FindManyOptions<E>): Promise<E[]> {
259264
return await this.performRead(async () => {
260265
const {cache, ...opts} = options
261266
const res = await this.em.find(target, opts)
262-
if (cache ?? this.cacheMode === 'ALL') this.persistEntities(target, res, options?.relations)
267+
if (cache ?? this.cacheMode === 'ALL') {
268+
const metadata = this.getEntityMetadata(target)
269+
for (const e of res) {
270+
this.cacheEntity(metadata, e)
271+
}
272+
}
263273
return res
264274
})
265275
}
@@ -269,11 +279,7 @@ export class Store {
269279
where: FindOptionsWhere<E> | FindOptionsWhere<E>[],
270280
cache?: boolean
271281
): Promise<E[]> {
272-
return await this.performRead(async () => {
273-
const res = await this.em.findBy(target, where)
274-
if (cache ?? this.cacheMode === 'ALL') this.persistEntities(target, res)
275-
return res
276-
})
282+
return await this.find(target, {where, cache})
277283
}
278284

279285
async findOne<E extends EntityLiteral>(
@@ -283,8 +289,11 @@ export class Store {
283289
return await this.performRead(async () => {
284290
const {cache, ...opts} = options
285291
const res = await this.em.findOne(target, opts).then(noNull)
286-
if (res != null && (cache ?? this.cacheMode === 'ALL'))
287-
this.persistEntities(target, res, options?.relations)
292+
if (cache ?? this.cacheMode === 'ALL') {
293+
const metadata = this.getEntityMetadata(target)
294+
const idOrEntity = res || getIdFromWhere(options.where)
295+
this.cacheEntity(metadata, idOrEntity)
296+
}
288297
return res
289298
})
290299
}
@@ -294,34 +303,23 @@ export class Store {
294303
where: FindOptionsWhere<E> | FindOptionsWhere<E>[],
295304
cache?: boolean
296305
): Promise<E | undefined> {
297-
return await this.performRead(async () => {
298-
const res = await this.em.findOneBy(target, where).then(noNull)
299-
if (res != null && (cache ?? this.cacheMode === 'ALL')) this.persistEntities(target, res)
300-
301-
return res
302-
})
306+
return await this.findOne(target, {where, cache})
303307
}
304308

305309
async findOneOrFail<E extends EntityLiteral>(target: EntityTarget<E>, options: FindOneOptions<E>): Promise<E> {
306-
return await this.performRead(async () => {
307-
const {cache, ...opts} = options
308-
const res = await this.em.findOneOrFail(target, opts)
309-
if (cache ?? this.cacheMode === 'ALL') this.persistEntities(target, res, options?.relations)
310-
311-
return res
312-
})
310+
const res = await this.findOne(target, options)
311+
if (res == null) throw new EntityNotFoundError(target, options.where)
312+
return res
313313
}
314314

315315
async findOneByOrFail<E extends EntityLiteral>(
316316
target: EntityTarget<E>,
317317
where: FindOptionsWhere<E> | FindOptionsWhere<E>[],
318318
cache?: boolean
319319
): Promise<E> {
320-
return await this.performRead(async () => {
321-
const res = await this.em.findOneByOrFail(target, where)
322-
if (cache || this.cacheMode === 'ALL') this.persistEntities(target, res)
323-
return res
324-
})
320+
const res = await this.findOneBy(target, where, cache)
321+
if (res == null) throw new EntityNotFoundError(target, where)
322+
return res
325323
}
326324

327325
async get<E extends EntityLiteral>(target: EntityTarget<E>, id: string): Promise<E | undefined>
@@ -331,28 +329,18 @@ export class Store {
331329
idOrOptions: string | GetOptions<E>
332330
): Promise<E | undefined> {
333331
const {id, relations} = parseGetOptions(idOrOptions)
334-
335332
const metadata = this.getEntityMetadata(target)
336333
let entity = this.state.get<E>(metadata, id, relations)
337334
if (entity !== undefined) return noNull(entity)
338-
339335
return await this.findOne(target, {where: {id} as any, relations, cache: true})
340336
}
341337

342-
async getOrFail<E extends EntityLiteral>(entityClass: EntityTarget<E>, id: string): Promise<E>
343-
async getOrFail<E extends EntityLiteral>(entityClass: EntityTarget<E>, options: GetOptions<E>): Promise<E>
344-
async getOrFail<E extends EntityLiteral>(
345-
entityClass: EntityTarget<E>,
346-
idOrOptions: string | GetOptions<E>
347-
): Promise<E> {
338+
async getOrFail<E extends EntityLiteral>(target: EntityTarget<E>, id: string): Promise<E>
339+
async getOrFail<E extends EntityLiteral>(target: EntityTarget<E>, options: GetOptions<E>): Promise<E>
340+
async getOrFail<E extends EntityLiteral>(target: EntityTarget<E>, idOrOptions: string | GetOptions<E>): Promise<E> {
348341
const options = parseGetOptions(idOrOptions)
349-
let e = await this.get(entityClass, options)
350-
351-
if (e == null) {
352-
const metadata = this.getEntityMetadata(entityClass)
353-
throw new Error(`Missing entity ${metadata.name} with id "${options.id}"`)
354-
}
355-
342+
let e = await this.get(target, options)
343+
if (e == null) throw new EntityNotFoundError(target, options.id)
356344
return e
357345
}
358346

@@ -365,89 +353,66 @@ export class Store {
365353

366354
this.pendingCommit = createFuture()
367355
try {
368-
const {upserts, inserts, deletes, extraUpserts} = this.state.computeChangeSets()
356+
await this.state.performUpdate(async ({upserts, inserts, deletes, extraUpserts}) => {
357+
for (const {metadata, entities} of upserts) {
358+
await this._upsert(metadata, entities)
359+
}
369360

370-
for (const {metadata, entities} of upserts) {
371-
await this._upsert(metadata, entities)
372-
}
361+
for (const {metadata, entities} of inserts) {
362+
await this._insert(metadata, entities)
363+
}
373364

374-
for (const {metadata, entities} of inserts) {
375-
await this._insert(metadata, entities)
376-
}
365+
for (const {metadata, ids} of deletes) {
366+
await this._delete(metadata, ids)
367+
}
377368

378-
for (const {metadata, ids} of deletes) {
379-
await this._delete(metadata, ids)
380-
}
369+
for (const {metadata, entities} of extraUpserts) {
370+
await this._upsert(metadata, entities)
371+
}
372+
})
381373

382-
for (const {metadata, entities} of extraUpserts) {
383-
await this._upsert(metadata, entities)
374+
if (this.resetMode === 'FLUSH' || reset) {
375+
this.reset()
384376
}
385-
386-
this.state.clear()
387377
} finally {
388378
this.pendingCommit.resolve()
389379
this.pendingCommit = undefined
390380
}
391-
392-
if (this.resetMode === 'FLUSH' || reset) {
393-
this.reset()
394-
}
395-
}
396-
397-
/**
398-
* @internal
399-
*/
400-
_close() {
401-
this.isClosed = true
402381
}
403382

404383
private async performRead<T>(cb: () => Promise<T>): Promise<T> {
405-
this.assetNotClosed()
406-
384+
this.assertNotClosed()
407385
if (this.flushMode === 'AUTO' || this.flushMode === 'ALWAYS') {
408386
await this.flush()
409387
}
410-
411388
return await cb()
412389
}
413390

414391
private async performWrite(cb: () => Promise<void>): Promise<void> {
392+
this.assertNotClosed()
415393
await this.pendingCommit?.promise()
416-
417-
this.assetNotClosed()
418-
419394
await cb()
420-
421395
if (this.flushMode === 'ALWAYS') {
422396
await this.flush()
423397
}
424398
}
425399

426-
private assetNotClosed() {
400+
private assertNotClosed() {
427401
assert(!this.isClosed, `too late to perform db updates, make sure you haven't forgot to await on db query`)
428402
}
429403

430-
private persistEntities<E extends EntityLiteral>(
431-
target: EntityTarget<E>,
432-
e: E | E[],
433-
relationMask?: FindOptionsRelations<any>
434-
) {
435-
const metadata = this.getEntityMetadata(target)
436-
437-
e = Array.isArray(e) ? e : [e]
438-
for (const entity of e) {
439-
traverseEntity({
440-
metadata,
441-
entity,
442-
relationMask: relationMask || null,
443-
cb: (e, md) => this.state?.persist(md, e),
444-
})
404+
private cacheEntity<E extends EntityLiteral>(metadata: EntityMetadata, entityOrId?: E | string) {
405+
if (entityOrId == null) {
406+
return
407+
} else if (typeof entityOrId === 'string') {
408+
this.state.settle(metadata, entityOrId)
409+
} else {
410+
traverseEntity(metadata, entityOrId, (e, md) => this.state.persist(md, e))
445411
}
446412
}
447413

448414
private getEntityMetadata(target: EntityTarget<any>) {
449-
const em = this.em
450-
return em.connection.getMetadata(target)
415+
return this.em.connection.getMetadata(target)
451416
}
452417
}
453418

@@ -458,3 +423,7 @@ function parseGetOptions<E>(idOrOptions: string | GetOptions<E>): GetOptions<E>
458423
return idOrOptions
459424
}
460425
}
426+
427+
function getIdFromWhere(where?: FindOptionsWhere<EntityLiteral>) {
428+
return typeof where?.id === 'string' ? where.id : undefined
429+
}

typeorm/typeorm-store/src/utils/cacheMap.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ export class CacheMap {
1818
return this.getEntityCache(metadata)?.get(id)
1919
}
2020

21-
exist(metadata: EntityMetadata, id: string): boolean {
21+
has(metadata: EntityMetadata, id: string): boolean {
2222
const cacheMap = this.getEntityCache(metadata)
2323
const cachedEntity = cacheMap.get(id)
2424
return !!cachedEntity?.value
2525
}
2626

27-
ensure(metadata: EntityMetadata, id: string): void {
27+
settle(metadata: EntityMetadata, id: string): void {
2828
const cacheMap = this.getEntityCache(metadata)
2929

3030
if (cacheMap.has(id)) return

typeorm/typeorm-store/src/utils/misc.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -89,43 +89,21 @@ export function mergeRelations<E extends ObjectLiteral>(
8989
return mergedObject
9090
}
9191

92-
export function traverseEntity({
93-
metadata,
94-
entity,
95-
relationMask,
96-
cb,
97-
}: {
98-
metadata: EntityMetadata
99-
entity: EntityLiteral
100-
relationMask: FindOptionsRelations<any> | null
92+
export function traverseEntity(
93+
metadata: EntityMetadata,
94+
entity: EntityLiteral,
10195
cb: (e: EntityLiteral, metadata: EntityMetadata) => void
102-
}) {
103-
if (relationMask != null) {
104-
for (const relation of metadata.relations) {
105-
const inverseRelationMask = relationMask[relation.propertyName]
106-
if (!inverseRelationMask) continue
107-
108-
const inverseEntity = relation.getEntityValue(entity)
109-
if (inverseEntity == null) continue
110-
111-
if (relation.isOneToMany || relation.isManyToMany) {
112-
if (!Array.isArray(inverseEntity)) continue
113-
for (const ie of inverseEntity) {
114-
traverseEntity({
115-
metadata: relation.inverseEntityMetadata,
116-
entity: ie,
117-
relationMask: inverseRelationMask === true ? null : inverseRelationMask,
118-
cb,
119-
})
120-
}
121-
} else {
122-
traverseEntity({
123-
metadata: relation.inverseEntityMetadata,
124-
entity: inverseEntity,
125-
relationMask: inverseRelationMask === true ? null : inverseRelationMask,
126-
cb,
127-
})
96+
) {
97+
for (const relation of metadata.relations) {
98+
const inverseEntity = relation.getEntityValue(entity)
99+
if (inverseEntity == null) continue
100+
101+
if (relation.isOneToMany || relation.isManyToMany) {
102+
for (const ie of inverseEntity) {
103+
traverseEntity(relation.inverseEntityMetadata, ie, cb)
128104
}
105+
} else {
106+
traverseEntity(relation.inverseEntityMetadata, inverseEntity, cb)
129107
}
130108
}
131109

0 commit comments

Comments
 (0)