Skip to content

Commit

Permalink
feat: deterministic id generator - utilities exposure fix (#504)
Browse files Browse the repository at this point in the history
affects: @joystream/hydra-processor
  • Loading branch information
ondratra authored Jul 21, 2022
1 parent 6dc2b7c commit 7b183b7
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 18 deletions.
22 changes: 11 additions & 11 deletions packages/hydra-processor/src/executor/EntityIdGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EntityManager } from 'typeorm'
import { ObjectType } from 'typedi'
import AsyncLock from 'async-lock'
import { DeterministicIdEntity } from '../entities'
import { DatabaseManager } from '@joystream/hydra-common'

export class EntityIdGenerator {
private entityClass: ObjectType<{ id: string }>
private entityRecord: DeterministicIdEntity | null = null
private entityRecord: DeterministicIdEntity | undefined = undefined
private static lock = new AsyncLock({ maxPending: 10000 })
// each id is 8 chars out of 36-size alphabet, giving us 2821109907456 possible ids (per entity type)
public static alphabet = '0123456789abcdefghijklmnopqrstuvwxyz'
Expand Down Expand Up @@ -48,14 +48,14 @@ export class EntityIdGenerator {
}

private async loadEntityRecord(
em: EntityManager
store: DatabaseManager
): Promise<DeterministicIdEntity> {
if (this.entityRecord) {
return this.entityRecord
}

// try load record from db
this.entityRecord = await em.findOne(DeterministicIdEntity, {
this.entityRecord = await store.get(DeterministicIdEntity, {
where: { className: this.entityClass.name },
})

Expand All @@ -69,24 +69,24 @@ export class EntityIdGenerator {
return this.entityRecord
}

private async generateNextEntityId(em: EntityManager): Promise<string> {
private async generateNextEntityId(store: DatabaseManager): Promise<string> {
// ensure entity record
const entityRecord = await this.loadEntityRecord(em)
const entityRecord = await this.loadEntityRecord(store)

// generate next id
const nextEntityId = EntityIdGenerator.entityIdAfter(entityRecord.highestId)

// save new id to db
entityRecord.highestId = nextEntityId
await em.save(entityRecord)
await store.save(entityRecord)

// return next id
return nextEntityId
}

public async getNextEntityId(em: EntityManager): Promise<string> {
public async getNextEntityId(store: DatabaseManager): Promise<string> {
return EntityIdGenerator.lock.acquire(this.entityClass.name, () =>
this.generateNextEntityId(em)
this.generateNextEntityId(store)
)
}
}
Expand All @@ -110,10 +110,10 @@ function ensureEntityIdGenerator(
Generates next sequential id for the given entity class.
*/
export async function generateNextId(
em: EntityManager,
store: DatabaseManager,
entityClass: ObjectType<{ id: string }>
): Promise<string> {
const idGenerator = ensureEntityIdGenerator(entityClass)

return idGenerator.getNextEntityId(em)
return idGenerator.getNextEntityId(store)
}
20 changes: 13 additions & 7 deletions packages/hydra-processor/src/executor/TransactionalExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { TxAwareBlockContext } from './tx-aware'
import { ObjectType } from 'typedi'
import { generateNextId } from './EntityIdGenerator'
import { DeterministicIdEntity } from '../entities'

const debug = Debug('hydra-processor:mappings-executor')

Expand Down Expand Up @@ -94,12 +95,12 @@ export function makeDatabaseManager(
entityManager: EntityManager,
blockData: BlockData
): DatabaseManager {
return {
const databaseManager: DatabaseManager = {
save: async <T>(entity: DeepPartial<T>): Promise<void> => {
// TODO: try to move ` as DeepPartial<T & Record<string, unknown>>` typecast to function definition
entity = await fillRequiredWarthogFields(
entity as DeepPartial<T & Record<string, unknown>>,
entityManager,
databaseManager,
blockData
)
await entityManager.save(entity)
Expand Down Expand Up @@ -129,7 +130,9 @@ export function makeDatabaseManager(
} // required by typeorm '0.3.5'
return await entityManager.find(entity, fixedOptions)
},
} as DatabaseManager
}

return databaseManager
}

/**
Expand All @@ -143,7 +146,7 @@ export function makeDatabaseManager(
*/
async function fillRequiredWarthogFields<T extends Record<string, unknown>>(
entity: DeepPartial<T>,
entityManager: EntityManager,
store: DatabaseManager,
{ block }: BlockData
): Promise<DeepPartial<T>> {
// TODO: find a way how to remove this; needed to limit possible `entity` types
Expand All @@ -152,16 +155,19 @@ async function fillRequiredWarthogFields<T extends Record<string, unknown>>(
throw new Error('Unexpected situation in prefilling Warthog fields')
}

// eslint-disable-next-line no-prototype-builtins
if (!entity.hasOwnProperty('id')) {
if (
// eslint-disable-next-line no-prototype-builtins
!entity.hasOwnProperty('id') &&
!(entity instanceof DeterministicIdEntity)
) {
const entityClass = (
entity as unknown as {
constructor: ObjectType<{ id: string }>
}
).constructor

Object.assign(entity, {
id: await generateNextId(entityManager, entityClass),
id: await generateNextId(store, entityClass),
})
}
// eslint-disable-next-line no-prototype-builtins
Expand Down
1 change: 1 addition & 0 deletions packages/hydra-processor/src/executor/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ let mappingsLookup: MappingsLookupService
export * from './IMappingExecutor'
export * from './IMappingsLookup'
export * from './tx-aware'
export { generateNextId } from './EntityIdGenerator' // expose for external usage

export async function getMappingExecutor(): Promise<IMappingExecutor> {
if (!mappingExecutor) {
Expand Down

0 comments on commit 7b183b7

Please sign in to comment.