Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Make from inclusive
Browse files Browse the repository at this point in the history
  • Loading branch information
sz-piotr committed Mar 22, 2024
1 parent 5424145 commit 245b3c8
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 64 deletions.
7 changes: 3 additions & 4 deletions packages/uif-example/src/blocks/BlockIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ export class BlockIndexer extends ChildIndexer {
await this.blockIndexerRepository.saveHeight(height)
}

override async update(currentHeight: number): Promise<number> {
const nextHeight = currentHeight + 1
const timestamp = nextHeight * ONE_HOUR_MS
override async update(from: number): Promise<number> {
const timestamp = from * ONE_HOUR_MS

const block = await this.blockService.getBlockNumberBefore(timestamp)
await this.blockRepository.save({ number: block, timestamp })

return nextHeight
return from
}

override async invalidate(targetHeight: number): Promise<number> {
Expand Down
13 changes: 6 additions & 7 deletions packages/uif-example/src/prices/PriceIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,17 @@ export class PriceIndexer extends MultiIndexer<PriceConfig> {
}

override async multiUpdate(
currentHeight: number,
targetHeight: number,
from: number,
to: number,
configurations: UpdateConfiguration<PriceConfig>[],
): Promise<number> {
const startHour = currentHeight + 1
// we only query 24 hours at a time
const endHour = Math.min(targetHeight, startHour + 23)
const adjustedTo = Math.min(to, from + 23)

const prices = await this.priceService.getHourlyPrices(
this.apiId,
startHour * ONE_HOUR_MS,
endHour * ONE_HOUR_MS,
from * ONE_HOUR_MS,
adjustedTo * ONE_HOUR_MS,
)

const dataToSave = configurations
Expand All @@ -64,7 +63,7 @@ export class PriceIndexer extends MultiIndexer<PriceConfig> {
})
await this.priceRepository.save(dataToSave)

return endHour
return adjustedTo
}

override async removeData(
Expand Down
24 changes: 12 additions & 12 deletions packages/uif/src/Indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,23 @@ export abstract class Indexer {
* Implements the main data fetching process. It is up to the indexer to
* decide how much data to fetch. For example given `.update(100, 200)`, the
* indexer can only fetch data up to 110 and return 110. The next time this
* method will be called with `.update(110, 200)`.
* method will be called with `.update(111, 200)`.
*
* @param currentHeight The height that the indexer has synced up to
* previously. This value is exclusive so the indexer should not fetch data
* for this height.
* @param from The height for which the indexer should start syncing data.
* This value is inclusive.
*
* @param targetHeight The height that the indexer should sync up to. This value is
* inclusive so the indexer should eventually fetch data for this height.
* @param to The height at which the indexer should end syncing data. This
* value is also inclusive so the indexer should eventually sync data for this
* height.
*
* @returns The height that the indexer has synced up to. Returning
* `currentHeight` means that the indexer has not synced any data. Returning
* a value greater than `currentHeight` means that the indexer has synced up
* to that height. Returning a value less than `currentHeight` will trigger
* `from` means that the indexer has synced a single data point. Returning
* a value greater than `from` means that the indexer has synced up
* to that height. Returning a value less than `from` will trigger
* invalidation down to the returned value. Returning a value greater than
* `targetHeight` is not permitted.
* `to` is not permitted.
*/
abstract update(currentHeight: number, targetHeight: number): Promise<number>
abstract update(from: number, to: number): Promise<number>

/**
* Responsible for invalidating data that was synced previously. It is
Expand Down Expand Up @@ -216,7 +216,7 @@ export abstract class Indexer {
// #region Child methods

private async executeUpdate(effect: UpdateEffect): Promise<void> {
const from = this.state.height
const from = this.state.height + 1
this.logger.info('Updating', { from, to: effect.targetHeight })
try {
const newHeight = await this.update(from, effect.targetHeight)
Expand Down
29 changes: 21 additions & 8 deletions packages/uif/src/indexers/multi/MultiIndexer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ describe(MultiIndexer.name, () => {
expect(testIndexer.removeData).not.toHaveBeenCalled()
expect(testIndexer.saveConfigurations).not.toHaveBeenCalled()
})

it('no synced data', async () => {
const testIndexer = new TestMultiIndexer(
[actual('a', 100, 400), actual('b', 200, 500)],
[],
)

const newHeight = await testIndexer.initialize()
expect(newHeight).toEqual(99)

expect(testIndexer.removeData).not.toHaveBeenCalled()
expect(testIndexer.saveConfigurations).not.toHaveBeenCalled()
})
})

describe(MultiIndexer.prototype.update.name, () => {
Expand Down Expand Up @@ -103,10 +116,10 @@ describe(MultiIndexer.name, () => {
)
await testIndexer.initialize()

const newHeight = await testIndexer.update(300, 600)
const newHeight = await testIndexer.update(301, 600)

expect(newHeight).toEqual(400)
expect(testIndexer.multiUpdate).toHaveBeenOnlyCalledWith(300, 400, [
expect(testIndexer.multiUpdate).toHaveBeenOnlyCalledWith(301, 400, [
update('a', 100, 400, false),
update('b', 200, 500, false),
])
Expand Down Expand Up @@ -137,7 +150,7 @@ describe(MultiIndexer.name, () => {
)
await testIndexer.initialize()

const newHeight = await testIndexer.update(400, 500)
const newHeight = await testIndexer.update(401, 500)

expect(newHeight).toEqual(500)
expect(testIndexer.multiUpdate).not.toHaveBeenCalled()
Expand All @@ -151,7 +164,7 @@ describe(MultiIndexer.name, () => {
)
await testIndexer.initialize()

const newHeight = await testIndexer.update(200, 500)
const newHeight = await testIndexer.update(201, 500)

expect(newHeight).toEqual(299)
expect(testIndexer.multiUpdate).not.toHaveBeenCalled()
Expand Down Expand Up @@ -208,8 +221,8 @@ describe(MultiIndexer.name, () => {
])

// Next range
expect(await testIndexer.update(200, 500)).toEqual(400)
expect(testIndexer.multiUpdate).toHaveBeenNthCalledWith(3, 200, 400, [
expect(await testIndexer.update(201, 500)).toEqual(400)
expect(testIndexer.multiUpdate).toHaveBeenNthCalledWith(3, 201, 400, [
update('b', 100, 400, false),
])
expect(testIndexer.saveConfigurations).toHaveBeenNthCalledWith(3, [
Expand Down Expand Up @@ -278,7 +291,7 @@ describe(MultiIndexer.name, () => {
testIndexer.multiUpdate.resolvesTo(150)

await expect(testIndexer.update(200, 300)).toBeRejectedWith(
/returned height must be between currentHeight and targetHeight/,
/returned height must be between from and to/,
)
})

Expand All @@ -292,7 +305,7 @@ describe(MultiIndexer.name, () => {
testIndexer.multiUpdate.resolvesTo(350)

await expect(testIndexer.update(200, 300)).toBeRejectedWith(
/returned height must be between currentHeight and targetHeight/,
/returned height must be between from and to/,
)
})
})
Expand Down
66 changes: 33 additions & 33 deletions packages/uif/src/indexers/multi/MultiIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,29 @@ export abstract class MultiIndexer<T> extends ChildIndexer {
* indexer can only fetch data up to 110 and return 110. The next time this
* method will be called with `.update(110, 200, [...])`.
*
* @param currentHeight The height that the indexer has synced up to
* previously. This value is exclusive so the indexer should not fetch data
* for this height. If the indexer hasn't synced anything previously this
* will equal the minimum height of all configurations - 1.
* @param from The height for which the indexer should start syncing data.
* This value is inclusive. If the indexer hasn't synced anything previously
* this will equal the minimum height of all configurations.
*
* @param targetHeight The height that the indexer should sync up to. This value is
* inclusive so the indexer should eventually fetch data for this height.
* @param to The height at which the indexer should end syncing data. This
* value is also inclusive so the indexer should eventually sync data for this
* height.
*
* @param configurations The configurations that the indexer should use to
* sync data. The configurations are guaranteed to be in the range of
* `currentHeight` and `targetHeight`. Some of those configurations might
* have been synced previously for this range. Those configurations
* will include the `hasData` flag set to `true`.
* `from` and `to`. Some of those configurations might have been synced
* previously for this range. Those configurations will include the `hasData`
* flag set to `true`.
*
* @returns The height that the indexer has synced up to. Returning
* `currentHeight` means that the indexer has not synced any data. Returning
* a value greater than `currentHeight` means that the indexer has synced up
* to that height. Returning a value less than `currentHeight` or greater than
* `targetHeight` is not permitted.
* `from` means that the indexer has synced a single data point. Returning
* a value greater than `from` means that the indexer has synced up
* to that height. Returning a value less than `from` or greater than
* `to` is not permitted.
*/
abstract multiUpdate(
currentHeight: number,
targetHeight: number,
from: number,
to: number,
configurations: UpdateConfiguration<T>[],
): Promise<number>

Expand Down Expand Up @@ -106,31 +106,32 @@ export abstract class MultiIndexer<T> extends ChildIndexer {
return safeHeight
}

async update(currentHeight: number, targetHeight: number): Promise<number> {
const range = findRange(this.ranges, currentHeight)
async update(from: number, to: number): Promise<number> {
const range = findRange(this.ranges, from)
if (range.configurations.length === 0) {
return Math.min(range.to, targetHeight)
return Math.min(range.to, to)
}

const { configurations, minCurrentHeight } = getConfigurationsInRange(
range,
this.saved,
currentHeight,
from,
)
const minTargetHeight = Math.min(range.to, targetHeight, minCurrentHeight)

const newHeight = await this.multiUpdate(
currentHeight,
minTargetHeight,
configurations,
)
if (newHeight < currentHeight || newHeight > minTargetHeight) {
const adjustedTo = Math.min(range.to, to, minCurrentHeight)

this.logger.info('Calling multiUpdate', {
from,
to: adjustedTo,
configurations: configurations.length,
})
const newHeight = await this.multiUpdate(from, adjustedTo, configurations)
if (newHeight < from || newHeight > adjustedTo) {
throw new Error(
'Programmer error, returned height must be between currentHeight and targetHeight.',
'Programmer error, returned height must be between from and to (both inclusive).',
)
}

if (newHeight > currentHeight) {
if (newHeight > from) {
updateSavedConfigurations(this.saved, configurations, newHeight)
await this.saveConfigurations(this.saved)
}
Expand All @@ -147,13 +148,12 @@ export abstract class MultiIndexer<T> extends ChildIndexer {
}
}

// TODO: test this function!
function findRange<T>(
ranges: ConfigurationRange<T>[],
currentHeight: number,
from: number,
): ConfigurationRange<T> {
const range = ranges.find(
(range) => range.from <= currentHeight + 1 && range.to > currentHeight,
)
const range = ranges.find((range) => range.from <= from && range.to >= from)
if (!range) {
throw new Error('Programmer error, there should always be a range')
}
Expand Down

0 comments on commit 245b3c8

Please sign in to comment.