Skip to content

Commit

Permalink
feat: rav metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
hopeyen committed Dec 7, 2023
1 parent 00bdbc5 commit e037443
Showing 1 changed file with 139 additions and 28 deletions.
167 changes: 139 additions & 28 deletions packages/indexer-common/src/allocations/query-fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ interface ReceiptMetrics {
vouchersRedeemDuration: Histogram<string>
vouchersBatchRedeemSize: Gauge<never>
voucherCollectedFees: Gauge<string>
successRavRedeems: Counter<string>
invalidRavRedeems: Counter<string>
failedRavRedeems: Counter<string>
ravsRedeemDuration: Histogram<string>
ravCollectedFees: Gauge<string>
}

export interface AllocationPartialVouchers {
Expand Down Expand Up @@ -147,7 +152,9 @@ export class AllocationReceiptCollector implements ReceiptCollector {
// flag during startup.
collector.startReceiptCollecting()
collector.startVoucherProcessing()
collector.startRAVProcessing()
if (collector.escrow) {
collector.startRAVProcessing()
}
await collector.queuePendingReceiptsFromDatabase()
return collector
}
Expand Down Expand Up @@ -735,16 +742,23 @@ export class AllocationReceiptCollector implements ReceiptCollector {

private async submitRAVs(ravs: SignedRav[]): Promise<void> {
const logger = this.logger.child({
function: 'submitVouchers()',
function: 'submitRAVs()',
voucherBatchSize: ravs.length,
})
if (this.escrow == null) {
logger.error(
`No escrow contracts, but this shouldn't happen as RAV process is only triggered when escrow is not null`,
{
ravs,
},
)
return
}
const escrow = this.escrow

logger.info(`Redeem query voucher batch on chain`, {
ravs,
})
const stopTimer = this.metrics.vouchersRedeemDuration.startTimer({
allocation: ravs[0].message.allocationId,
})

const hexPrefix = (bytes: string): string =>
bytes.startsWith('0x') ? bytes : `0x${bytes}`
Expand All @@ -757,33 +771,95 @@ export class AllocationReceiptCollector implements ReceiptCollector {

// Redeem RAV one-by-one as no plual version available
for (const rav of onchainRAVs) {
const allocationId = rav.message.allocationId
// Look up allocation
const allocation = (await this.allocations.value()).find(
(a) => a.id == allocationId,
)
// Fail query outright if we have no signer for this allocation
if (allocation === undefined) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = Error(`Unable to match allocation`) as any
error.status = 500
throw error
const stopTimer = this.metrics.ravsRedeemDuration.startTimer({
allocation: rav.message.allocationId,
})
try {
// Look up allocation
const allocation = (await this.allocations.value()).find(
(a) => a.id == rav.message.allocationId,
)
// Fail query outright if we have no signer for this allocation
if (allocation === undefined) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error = Error(`Unable to match allocation`) as any
error.status = 500
throw error
}
// compute allocation id proof
const proof = await allocationIdProof(
allocationSigner(this.transactionManager.wallet, allocation),
this.transactionManager.wallet.address,
rav.message.allocationId,
)
this.logger.debug(`Computed allocationIdProof`, {
allocationId: rav.message.allocationId,
proof,
})
// Submit the signed RAV on chain
const txReceipt = await escrow.executeTransaction(
() => escrow.estimateGas.redeem(rav, proof),
async (gasLimit: BigNumberish) =>
escrow.redeem(rav, proof, {
gasLimit,
}),
logger.child({ action: 'redeem' }),
)

// get tx receipt and post process
if (txReceipt === 'paused' || txReceipt === 'unauthorized') {
this.metrics.invalidRavRedeems.inc({ allocation: rav.message.allocationId })
return
}
this.metrics.ravCollectedFees.set(
{ allocation: rav.message.allocationId },
parseFloat(rav.message.valueAggregate.toString()),
)
} catch (err) {
this.metrics.failedRavRedeems.inc({ allocation: rav.message.allocationId })
logger.error(`Failed to redeem RAV`, {
err: indexerError(IndexerErrorCode.IE055, err),
})
return
}
const signer = allocationSigner(this.transactionManager.wallet, allocation)
// compute allocation id proof
const proof = await allocationIdProof(
signer,
this.transactionManager.wallet.address,
allocationId,
stopTimer()
}

// Postprocess obsolete RAVs from the database
logger.info(`Successfully redeemed RAV, delete local copy`)
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await this.models.allocationSummaries.sequelize!.transaction(
async (transaction) => {
for (const rav of ravs) {
const [summary] = await ensureAllocationSummary(
this.models,
toAddress(rav.message.allocationId),
transaction,
this.protocolNetwork,
)
summary.withdrawnFees = BigNumber.from(summary.withdrawnFees)
.add(rav.message.valueAggregate)
.toString()
await summary.save({ transaction })
}
},
)
this.logger.debug(`Got allocationIdProof`, {
proof,
})
// submit escrow redeem with signed rav and proof

// get tx receipt and post process
await this.models.receiptAggregateVouchers.destroy({
where: {
allocationId: ravs.map((rav) => rav.message.allocationId),
},
})
ravs.map((rav) =>
this.metrics.successRavRedeems.inc({ allocation: rav.message.allocationId }),
)
logger.info(`Successfully deleted local RAV copy`)
} catch (err) {
logger.warn(`Failed to delete local RAV copy, will try again later`, {
err,
})
}
stopTimer()
}

public async queuePendingReceiptsFromDatabase(): Promise<void> {
Expand Down Expand Up @@ -931,6 +1007,41 @@ const registerReceiptMetrics = (metrics: Metrics, networkIdentifier: string) =>
registers: [metrics.registry],
labelNames: ['allocation'],
}),

successRavRedeems: new metrics.client.Counter({
name: `indexer_agent_rav_exchanges_ok_${networkIdentifier}`,
help: 'Successfully redeemed ravs',
registers: [metrics.registry],
labelNames: ['allocation'],
}),

invalidRavRedeems: new metrics.client.Counter({
name: `indexer_agent_rav_exchanges_invalid_${networkIdentifier}`,
help: 'Invalid ravs redeems - tx paused or unauthorized',
registers: [metrics.registry],
labelNames: ['allocation'],
}),

failedRavRedeems: new metrics.client.Counter({
name: `indexer_agent_rav_redeems_failed_${networkIdentifier}`,
help: 'Failed redeems for ravs',
registers: [metrics.registry],
labelNames: ['allocation'],
}),

ravsRedeemDuration: new metrics.client.Histogram({
name: `indexer_agent_ravs_redeem_duration_${networkIdentifier}`,
help: 'Duration of redeeming ravs',
registers: [metrics.registry],
labelNames: ['allocation'],
}),

ravCollectedFees: new metrics.client.Gauge({
name: `indexer_agent_rav_collected_fees_${networkIdentifier}`,
help: 'Amount of query fees collected for a rav',
registers: [metrics.registry],
labelNames: ['allocation'],
}),
})

interface GatewayRoutes {
Expand Down

0 comments on commit e037443

Please sign in to comment.