Skip to content

Commit b198ce3

Browse files
committed
feat: trying lightweight list batch update
1 parent 1bdfebe commit b198ce3

File tree

6 files changed

+77
-112
lines changed

6 files changed

+77
-112
lines changed

schema.graphql

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ type Indexer @entity {
505505
"Delegators to this Indexer"
506506
delegators: [DelegatedStake!]! @derivedFrom(field: "indexer")
507507

508+
"Delegator relation entities. For internal usage for performance reasons"
509+
relations: [IndexerDelegatedStakeRelation!]! @derivedFrom(field: "indexer")
510+
508511
"CURRENT tokens delegated to the indexer"
509512
delegatedTokens: BigInt!
510513

@@ -1071,16 +1074,7 @@ type IndexerDelegatedStakeRelation @entity {
10711074
id: Bytes!
10721075

10731076
"Indexer entity where the delegation resides"
1074-
indexer: Indexer!
1075-
1076-
"Delegator entity owner of said delegation stake"
1077-
delegator: Delegator!
1078-
1079-
"DelegatedStake entity that represents the delegation"
1080-
stake: DelegatedStake!
1081-
1082-
"Whether the delegation is active or not. Useful to avoid updating non-active delegations without deleting the entity."
1083-
active: Boolean!
1077+
indexer: Indexer
10841078
}
10851079

10861080
type IndexerDailyData @entity {

src/mappings/gns.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,6 @@ export function handleSubgraphPublished(event: SubgraphPublished): void {
165165
subgraphVersion.subgraphDeployment = subgraphDeploymentID
166166
subgraphVersion.version = versionNumber.toI32()
167167
subgraphVersion.createdAt = event.block.timestamp.toI32()
168-
let hexHash = changetype<Bytes>(addQm(event.params.versionMetadata))
169-
let base58Hash = hexHash.toBase58()
170168
subgraphVersion.metadataHash = event.params.versionMetadata
171169
subgraphVersion.save()
172170
}
@@ -637,8 +635,6 @@ export function handleSubgraphVersionUpdated(event: SubgraphVersionUpdated): voi
637635
subgraphVersion.subgraphDeployment = subgraphDeploymentID
638636
subgraphVersion.version = versionNumber.toI32()
639637
subgraphVersion.createdAt = event.block.timestamp.toI32()
640-
let hexHash = changetype<Bytes>(addQm(event.params.versionMetadata))
641-
let base58Hash = hexHash.toBase58()
642638
subgraphVersion.metadataHash = event.params.versionMetadata
643639
subgraphVersion.save()
644640
}

src/mappings/helpers.ts

Lines changed: 69 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,6 @@ export function createOrLoadDelegatedStake(
214214

215215
if (delegatedStake == null) {
216216
let indexerEntity = Indexer.load(indexer)!
217-
let relationId = compoundId(
218-
indexer,
219-
changetype<Bytes>(Bytes.fromBigInt(indexerEntity.delegatorsCount)),
220-
)
221217
delegatedStake = new DelegatedStake(id)
222218
delegatedStake.indexer = indexer
223219
delegatedStake.delegator = delegator.id
@@ -236,15 +232,11 @@ export function createOrLoadDelegatedStake(
236232
delegatedStake.originalDelegation = BIGDECIMAL_ZERO
237233
delegatedStake.currentDelegation = BIGDECIMAL_ZERO
238234
delegatedStake.createdAt = timestamp
239-
delegatedStake.relation = relationId
235+
delegatedStake.relation = id
240236
delegatedStake.save()
241237

242-
let relation = new IndexerDelegatedStakeRelation(relationId)
243-
244-
relation.indexer = indexerEntity.id
245-
relation.stake = delegatedStake.id
246-
relation.delegator = delegator.id
247-
relation.active = true
238+
let relation = new IndexerDelegatedStakeRelation(id) // minimal amount of data to avoid running out of memory
239+
relation.indexer = indexerEntity.id // only needed for field derivation and active status
248240
relation.save()
249241

250242
indexerEntity.delegatorsCount = indexerEntity.delegatorsCount.plus(BIGINT_ONE)
@@ -623,37 +615,35 @@ export function batchUpdateDelegatorsForIndexer(indexerId: Bytes, timestamp: Big
623615
// Loading it again here to make sure we have the latest up to date data on the entity.
624616
let indexer = Indexer.load(indexerId)!
625617
// pre-calculates a lot of data for all delegators that exists for a specific indexer
626-
// using already existing links with the indexer-delegatedStake relations
627-
for (let i = 0; i < indexer.delegatorsCount.toI32(); i++) {
628-
let relationId = compoundId(indexer.id, changetype<Bytes>(Bytes.fromBigInt(BigInt.fromString(i.toString()))))
629-
let relation = IndexerDelegatedStakeRelation.load(relationId)!
630-
if (relation.active) {
631-
let delegatedStake = DelegatedStake.load(relation.stake)!
632-
let delegator = Delegator.load(delegatedStake.delegator)!
633-
// Only update core entities if there's a change in the exchange rate
634-
if (delegatedStake.latestIndexerExchangeRate != indexer.delegationExchangeRate) {
635-
let oldUnrealizedRewards = delegatedStake.unrealizedRewards
636-
637-
delegatedStake.latestIndexerExchangeRate = indexer.delegationExchangeRate
638-
delegatedStake.currentDelegation =
639-
delegatedStake.latestIndexerExchangeRate * delegatedStake.shareAmount.toBigDecimal()
640-
delegatedStake.unrealizedRewards = avoidNegativeRoundingError(
641-
delegatedStake.currentDelegation - delegatedStake.originalDelegation,
642-
)
643-
delegatedStake.save()
644-
645-
let diffUnrealized = delegatedStake.unrealizedRewards - oldUnrealizedRewards
646-
647-
delegator.totalUnrealizedRewards = avoidNegativeRoundingError(
648-
delegator.totalUnrealizedRewards.plus(diffUnrealized),
649-
)
650-
delegator.currentDelegation = delegator.currentDelegation.plus(diffUnrealized)
651-
delegator.save()
652-
}
653-
654-
getAndUpdateDelegatedStakeDailyData(delegatedStake as DelegatedStake, timestamp)
655-
getAndUpdateDelegatorDailyData(delegator as Delegator, timestamp)
618+
// uses lightweight relation entity to derive the full list. hopefully it doesn't run out of memory on big deleg count indexers
619+
let relations = indexer.relations.load()
620+
621+
for (let i = 0; i < relations.length; i++) {
622+
let delegatedStake = DelegatedStake.load(relations[i].id)!
623+
let delegator = Delegator.load(delegatedStake.delegator)!
624+
// Only update core entities if there's a change in the exchange rate
625+
if (delegatedStake.latestIndexerExchangeRate != indexer.delegationExchangeRate) {
626+
let oldUnrealizedRewards = delegatedStake.unrealizedRewards
627+
628+
delegatedStake.latestIndexerExchangeRate = indexer.delegationExchangeRate
629+
delegatedStake.currentDelegation =
630+
delegatedStake.latestIndexerExchangeRate.times(delegatedStake.shareAmount.toBigDecimal())
631+
delegatedStake.unrealizedRewards = avoidNegativeRoundingError(
632+
delegatedStake.currentDelegation.minus(delegatedStake.originalDelegation),
633+
)
634+
delegatedStake.save()
635+
636+
let diffUnrealized = delegatedStake.unrealizedRewards.minus(oldUnrealizedRewards)
637+
638+
delegator.totalUnrealizedRewards = avoidNegativeRoundingError(
639+
delegator.totalUnrealizedRewards.plus(diffUnrealized),
640+
)
641+
delegator.currentDelegation = delegator.currentDelegation.plus(diffUnrealized)
642+
delegator.save()
656643
}
644+
645+
getAndUpdateDelegatedStakeDailyData(delegatedStake as DelegatedStake, timestamp)
646+
getAndUpdateDelegatorDailyData(delegator as Delegator, timestamp)
657647
}
658648
}
659649

@@ -663,16 +653,13 @@ export function getAndUpdateNetworkDailyData(
663653
): GraphNetworkDailyData {
664654
let dayNumber = timestamp.toI32() / SECONDS_PER_DAY - LAUNCH_DAY
665655
let id = compoundId(entity.id, Bytes.fromI32(dayNumber))
666-
let dailyData = GraphNetworkDailyData.load(id)
667656

668-
if (dailyData == null) {
669-
dailyData = new GraphNetworkDailyData(id)
670-
671-
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
672-
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
673-
dailyData.dayNumber = dayNumber
674-
dailyData.network = entity.id
675-
}
657+
// not checking for previous entity since we don't want to waste a load and data will be overwritten anyways
658+
let dailyData = new GraphNetworkDailyData(id)
659+
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
660+
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
661+
dailyData.dayNumber = dayNumber
662+
dailyData.network = entity.id
676663

677664
dailyData.delegationRatio = entity.delegationRatio
678665
dailyData.totalTokensStaked = entity.totalTokensStaked
@@ -706,18 +693,15 @@ export function getAndUpdateNetworkDailyData(
706693
export function getAndUpdateIndexerDailyData(entity: Indexer, timestamp: BigInt): IndexerDailyData {
707694
let dayNumber = timestamp.toI32() / SECONDS_PER_DAY - LAUNCH_DAY
708695
let id = compoundId(entity.id, Bytes.fromI32(dayNumber))
709-
let dailyData = IndexerDailyData.load(id)
710696

711-
if (dailyData == null) {
712-
dailyData = new IndexerDailyData(id)
713-
714-
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
715-
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
716-
dailyData.dayNumber = dayNumber
717-
dailyData.indexer = entity.id
718-
dailyData.netDailyDelegatedTokens = BIGINT_ZERO
719-
dailyData.delegatorsCount = BIGINT_ZERO
720-
}
697+
// not checking for previous entity since we don't want to waste a load and data will be overwritten anyways
698+
let dailyData = new IndexerDailyData(id)
699+
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
700+
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
701+
dailyData.dayNumber = dayNumber
702+
dailyData.indexer = entity.id
703+
dailyData.netDailyDelegatedTokens = BIGINT_ZERO
704+
dailyData.delegatorsCount = BIGINT_ZERO
721705

722706
dailyData.stakedTokens = entity.stakedTokens
723707
dailyData.delegatedTokens = entity.delegatedTokens
@@ -748,20 +732,17 @@ export function getAndUpdateDelegatedStakeDailyData(
748732
): DelegatedStakeDailyData {
749733
let dayNumber = timestamp.toI32() / SECONDS_PER_DAY - LAUNCH_DAY
750734
let stakeId = compoundId(stakeEntity.id, Bytes.fromI32(dayNumber))
751-
let stakeDailyData = DelegatedStakeDailyData.load(stakeId)
752-
753-
if (stakeDailyData == null) {
754-
stakeDailyData = new DelegatedStakeDailyData(stakeId)
755-
756-
stakeDailyData.dayStart = BigInt.fromI32(
757-
(timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY,
758-
)
759-
stakeDailyData.dayEnd = stakeDailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
760-
stakeDailyData.dayNumber = dayNumber
761-
stakeDailyData.stake = stakeEntity.id
762-
stakeDailyData.delegator = stakeEntity.delegator
763-
stakeDailyData.indexer = stakeEntity.indexer
764-
}
735+
736+
// not checking for previous entity since we don't want to waste a load and data will be overwritten anyways
737+
let stakeDailyData = new DelegatedStakeDailyData(stakeId)
738+
stakeDailyData.dayStart = BigInt.fromI32(
739+
(timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY,
740+
)
741+
stakeDailyData.dayEnd = stakeDailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
742+
stakeDailyData.dayNumber = dayNumber
743+
stakeDailyData.stake = stakeEntity.id
744+
stakeDailyData.delegator = stakeEntity.delegator
745+
stakeDailyData.indexer = stakeEntity.indexer
765746

766747
stakeDailyData.stakedTokens = stakeEntity.stakedTokens
767748
stakeDailyData.lockedTokens = stakeEntity.lockedTokens
@@ -784,16 +765,13 @@ export function getAndUpdateDelegatorDailyData(
784765
): DelegatorDailyData {
785766
let dayNumber = timestamp.toI32() / SECONDS_PER_DAY - LAUNCH_DAY
786767
let id = compoundId(entity.id, Bytes.fromI32(dayNumber))
787-
let dailyData = DelegatorDailyData.load(id)
788-
789-
if (dailyData == null) {
790-
dailyData = new DelegatorDailyData(id)
791-
792-
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
793-
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
794-
dailyData.dayNumber = dayNumber
795-
dailyData.delegator = entity.id
796-
}
768+
769+
// not checking for previous entity since we don't want to waste a load and data will be overwritten anyways
770+
let dailyData = new DelegatorDailyData(id)
771+
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
772+
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
773+
dailyData.dayNumber = dayNumber
774+
dailyData.delegator = entity.id
797775

798776
dailyData.stakesCount = entity.stakesCount
799777
dailyData.activeStakesCount = entity.activeStakesCount
@@ -814,16 +792,13 @@ export function getAndUpdateSubgraphDeploymentDailyData(
814792
): SubgraphDeploymentDailyData {
815793
let dayId = timestamp.toI32() / SECONDS_PER_DAY - LAUNCH_DAY
816794
let id = compoundId(entity.id, Bytes.fromI32(dayId))
817-
let dailyData = SubgraphDeploymentDailyData.load(id)
818-
819-
if (dailyData == null) {
820-
dailyData = new SubgraphDeploymentDailyData(id)
821795

822-
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
823-
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
824-
dailyData.dayNumber = dayId
825-
dailyData.subgraphDeployment = entity.id
826-
}
796+
// not checking for previous entity since we don't want to waste a load and data will be overwritten anyways
797+
let dailyData = new SubgraphDeploymentDailyData(id)
798+
dailyData.dayStart = BigInt.fromI32((timestamp.toI32() / SECONDS_PER_DAY) * SECONDS_PER_DAY)
799+
dailyData.dayEnd = dailyData.dayStart.plus(BigInt.fromI32(SECONDS_PER_DAY))
800+
dailyData.dayNumber = dayId
801+
dailyData.subgraphDeployment = entity.id
827802

828803
dailyData.stakedTokens = entity.stakedTokens
829804
dailyData.signalledTokens = entity.signalledTokens

src/mappings/l1staking.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export function handleDelegationTransferredToL2(event: DelegationTransferredToL2
156156

157157
// De-activate relation with indexer after batch update, so last datapoints are created properly
158158
let relation = IndexerDelegatedStakeRelation.load(delegation.relation)!
159-
relation.active = false
159+
relation.indexer = null
160160
relation.save()
161161
getAndUpdateNetworkDailyData(graphNetwork as GraphNetwork, event.block.timestamp)
162162
}

src/mappings/staking.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ export function handleStakeDelegated(event: StakeDelegated): void {
271271
// We wouldn't want to re-activate the relation in that case
272272
if (!delegatedStake.shareAmount.equals(BIGINT_ZERO)) {
273273
let relation = IndexerDelegatedStakeRelation.load(delegatedStake.relation)!
274-
relation.active = true
274+
relation.indexer = indexer.id
275275
relation.save()
276276
}
277277

@@ -375,7 +375,7 @@ export function handleStakeDelegatedLocked(event: StakeDelegatedLocked): void {
375375
// De-activate relation with indexer after batch update, so last datapoints are created properly
376376
if (delegatedStake.shareAmount.equals(BIGINT_ZERO)) {
377377
let relation = IndexerDelegatedStakeRelation.load(delegatedStake.relation)!
378-
relation.active = false
378+
relation.indexer = null
379379
relation.save()
380380
}
381381
getAndUpdateNetworkDailyData(graphNetwork as GraphNetwork, event.block.timestamp)

subgraph.template.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
specVersion: 1.0.0
1+
specVersion: 1.1.0
22
description: Graph Network analytics subgraph ({{network}})
33
repository: https://github.com/graphprotocol/graph-network-analytics-subgraph
44
features:

0 commit comments

Comments
 (0)