Skip to content

Commit b44249d

Browse files
committed
save
1 parent 5403ef5 commit b44249d

File tree

6 files changed

+94
-148
lines changed

6 files changed

+94
-148
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 & 102 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'
@@ -96,16 +103,10 @@ export class Store {
96103
this.cacheMode = cacheMode
97104
}
98105

99-
/**
100-
* @internal
101-
*/
102106
get _em() {
103107
return this.em
104108
}
105109

106-
/**
107-
* @internal
108-
*/
109110
get _state() {
110111
return this.state
111112
}
@@ -237,7 +238,7 @@ export class Store {
237238
private async _delete(metadata: EntityMetadata, ids: string[]) {
238239
this.logger?.debug(`delete ${metadata.name} ${ids.length} entities`)
239240
await this.changes?.writeDelete(metadata, ids)
240-
await this.em.delete(metadata.target, ids) // TODO: should be split by chunks too?
241+
await this.em.delete(metadata.target, ids) // NOTE: should be split by chunks too?
241242
}
242243

243244
async count<E extends EntityLiteral>(target: EntityTarget<E>, options?: FindManyOptions<E>): Promise<number> {
@@ -250,16 +251,19 @@ export class Store {
250251
target: EntityTarget<E>,
251252
where: FindOptionsWhere<E> | FindOptionsWhere<E>[]
252253
): Promise<number> {
253-
return await this.performRead(async () => {
254-
return await this.em.countBy(target, where)
255-
})
254+
return await this.count(target, {where})
256255
}
257256

258257
async find<E extends EntityLiteral>(target: EntityTarget<E>, options: FindManyOptions<E>): Promise<E[]> {
259258
return await this.performRead(async () => {
260259
const {cache, ...opts} = options
261260
const res = await this.em.find(target, opts)
262-
if (cache ?? this.cacheMode === 'ALL') this.persistEntities(target, res, options?.relations)
261+
if (cache ?? this.cacheMode === 'ALL') {
262+
const metadata = this.getEntityMetadata(target)
263+
for (const e of res) {
264+
this.cacheEntity(metadata, e)
265+
}
266+
}
263267
return res
264268
})
265269
}
@@ -269,11 +273,7 @@ export class Store {
269273
where: FindOptionsWhere<E> | FindOptionsWhere<E>[],
270274
cache?: boolean
271275
): 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-
})
276+
return await this.find(target, {where, cache})
277277
}
278278

279279
async findOne<E extends EntityLiteral>(
@@ -283,8 +283,11 @@ export class Store {
283283
return await this.performRead(async () => {
284284
const {cache, ...opts} = options
285285
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)
286+
if (cache ?? this.cacheMode === 'ALL') {
287+
const metadata = this.getEntityMetadata(target)
288+
const idOrEntity = res || getIdFromWhere(options.where)
289+
this.cacheEntity(metadata, idOrEntity)
290+
}
288291
return res
289292
})
290293
}
@@ -294,34 +297,23 @@ export class Store {
294297
where: FindOptionsWhere<E> | FindOptionsWhere<E>[],
295298
cache?: boolean
296299
): 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-
})
300+
return await this.findOne(target, {where, cache})
303301
}
304302

305303
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-
})
304+
const res = await this.findOne(target, options)
305+
if (res == null) throw new EntityNotFoundError(target, options.where)
306+
return res
313307
}
314308

315309
async findOneByOrFail<E extends EntityLiteral>(
316310
target: EntityTarget<E>,
317311
where: FindOptionsWhere<E> | FindOptionsWhere<E>[],
318312
cache?: boolean
319313
): 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-
})
314+
const res = await this.findOneBy(target, where, cache)
315+
if (res == null) throw new EntityNotFoundError(target, where)
316+
return res
325317
}
326318

327319
async get<E extends EntityLiteral>(target: EntityTarget<E>, id: string): Promise<E | undefined>
@@ -331,28 +323,18 @@ export class Store {
331323
idOrOptions: string | GetOptions<E>
332324
): Promise<E | undefined> {
333325
const {id, relations} = parseGetOptions(idOrOptions)
334-
335326
const metadata = this.getEntityMetadata(target)
336327
let entity = this.state.get<E>(metadata, id, relations)
337328
if (entity !== undefined) return noNull(entity)
338-
339329
return await this.findOne(target, {where: {id} as any, relations, cache: true})
340330
}
341331

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> {
332+
async getOrFail<E extends EntityLiteral>(target: EntityTarget<E>, id: string): Promise<E>
333+
async getOrFail<E extends EntityLiteral>(target: EntityTarget<E>, options: GetOptions<E>): Promise<E>
334+
async getOrFail<E extends EntityLiteral>(target: EntityTarget<E>, idOrOptions: string | GetOptions<E>): Promise<E> {
348335
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-
336+
let e = await this.get(target, options)
337+
if (e == null) throw new EntityNotFoundError(target, options.id)
356338
return e
357339
}
358340

@@ -365,89 +347,66 @@ export class Store {
365347

366348
this.pendingCommit = createFuture()
367349
try {
368-
const {upserts, inserts, deletes, extraUpserts} = this.state.computeChangeSets()
350+
await this.state.performUpdate(async ({upserts, inserts, deletes, extraUpserts}) => {
351+
for (const {metadata, entities} of upserts) {
352+
await this._upsert(metadata, entities)
353+
}
369354

370-
for (const {metadata, entities} of upserts) {
371-
await this._upsert(metadata, entities)
372-
}
355+
for (const {metadata, entities} of inserts) {
356+
await this._insert(metadata, entities)
357+
}
373358

374-
for (const {metadata, entities} of inserts) {
375-
await this._insert(metadata, entities)
376-
}
359+
for (const {metadata, ids} of deletes) {
360+
await this._delete(metadata, ids)
361+
}
377362

378-
for (const {metadata, ids} of deletes) {
379-
await this._delete(metadata, ids)
380-
}
363+
for (const {metadata, entities} of extraUpserts) {
364+
await this._upsert(metadata, entities)
365+
}
366+
})
381367

382-
for (const {metadata, entities} of extraUpserts) {
383-
await this._upsert(metadata, entities)
368+
if (this.resetMode === 'FLUSH' || reset) {
369+
this.reset()
384370
}
385-
386-
this.state.clear()
387371
} finally {
388372
this.pendingCommit.resolve()
389373
this.pendingCommit = undefined
390374
}
391-
392-
if (this.resetMode === 'FLUSH' || reset) {
393-
this.reset()
394-
}
395-
}
396-
397-
/**
398-
* @internal
399-
*/
400-
_close() {
401-
this.isClosed = true
402375
}
403376

404377
private async performRead<T>(cb: () => Promise<T>): Promise<T> {
405-
this.assetNotClosed()
406-
378+
this.assertNotClosed()
407379
if (this.flushMode === 'AUTO' || this.flushMode === 'ALWAYS') {
408380
await this.flush()
409381
}
410-
411382
return await cb()
412383
}
413384

414385
private async performWrite(cb: () => Promise<void>): Promise<void> {
386+
this.assertNotClosed()
415387
await this.pendingCommit?.promise()
416-
417-
this.assetNotClosed()
418-
419388
await cb()
420-
421389
if (this.flushMode === 'ALWAYS') {
422390
await this.flush()
423391
}
424392
}
425393

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

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-
})
398+
private cacheEntity<E extends EntityLiteral>(metadata: EntityMetadata, entityOrId?: E | string) {
399+
if (entityOrId == null) {
400+
return
401+
} else if (typeof entityOrId === 'string') {
402+
this.state.settle(metadata, entityOrId)
403+
} else {
404+
traverseEntity(metadata, entityOrId, (e, md) => this.state.persist(md, e))
445405
}
446406
}
447407

448408
private getEntityMetadata(target: EntityTarget<any>) {
449-
const em = this.em
450-
return em.connection.getMetadata(target)
409+
return this.em.connection.getMetadata(target)
451410
}
452411
}
453412

@@ -458,3 +417,7 @@ function parseGetOptions<E>(idOrOptions: string | GetOptions<E>): GetOptions<E>
458417
return idOrOptions
459418
}
460419
}
420+
421+
function getIdFromWhere(where?: FindOptionsWhere<EntityLiteral>) {
422+
return typeof where?.id === 'string' ? where.id : undefined
423+
}

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)