Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// TODO: move to sdk-nestjs
import { Cron } from '@nestjs/schedule';

const timerRegistryKey = Symbol('__extendedCronTimers');

export function ExtendedCron(expression: string): MethodDecorator {
return function (target: any, propertyKey, descriptor) {
const methodName = propertyKey as string;

// Check if it's a 7-part expression: "*/100 * * * * * *"
const parts = expression.trim().split(/\s+/);
const isMillisecondCron = parts.length === 7;

if (!isMillisecondCron) {
// Use standard @Cron for regular cron syntax
return Cron(expression)(target, propertyKey, descriptor);
}

// Extract millisecond interval
const msExpr = parts[0]; // e.g. */100
const match = msExpr.match(/^\*\/(\d+)$/);
if (!match) {
throw new Error(
`Invalid millisecond cron format: ${expression}. Expected format like '*/100 * * * * * *'`
);
}

const intervalMs = parseInt(match[1], 10);

// Handle onModuleInit / Destroy
const originalInit = target['onModuleInit'];
target['onModuleInit'] = function (...args: any[]) {
if (typeof originalInit === 'function') {
originalInit.apply(this, args);
}

if (!this[timerRegistryKey]) {
this[timerRegistryKey] = new Map<string, NodeJS.Timer>();
}

const timers: Map<string, NodeJS.Timer> = this[timerRegistryKey];

if (!timers.has(methodName)) {
const timer = setInterval(() => {
this[methodName]();
}, intervalMs);

timers.set(methodName, timer);
}
};

const originalDestroy = target['onModuleDestroy'];
target['onModuleDestroy'] = function (...args: any[]) {
if (typeof originalDestroy === 'function') {
originalDestroy.apply(this, args);
}

const timers: Map<string, NodeJS.Timer> = this[timerRegistryKey];
if (timers?.has(methodName)) {
clearInterval(timers.get(methodName)!);
timers.delete(methodName);
}
};
};
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { CacheService } from "@multiversx/sdk-nestjs-cache";
import { Inject, Injectable, Logger } from "@nestjs/common";
import { ClientProxy } from "@nestjs/microservices";
import { Cron } from "@nestjs/schedule";
import { ApiConfigService } from "src/common/api-config/api.config.service";
import { CacheInfo } from "src/utils/cache.info";
import { LogMetricsEvent } from "src/common/entities/log.metrics.event";
import { EventEmitter2 } from "@nestjs/event-emitter";
import { MetricsEvents } from "src/utils/metrics-events.constants";
import { TransactionProcessor } from "@multiversx/sdk-transaction-processor";
import { LogTopic } from "@multiversx/sdk-transaction-processor/lib/types/log-topic";
import { Lock } from "@multiversx/sdk-nestjs-common";
import { ExtendedCron } from "./decorators/extended-cron.decorator";

@Injectable()
export class TransactionCompletedService {
Expand All @@ -23,7 +24,8 @@ export class TransactionCompletedService {
private readonly eventEmitter: EventEmitter2,
) { }

@Cron('*/1 * * * * *')
@ExtendedCron('*/500 * * * * * *') // each 500ms
@Lock({ name: 'Completed transactions', verbose: true })
async handleNewTransactions() {
if (this.isProcessing) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Inject, Injectable } from "@nestjs/common";
import { ClientProxy } from "@nestjs/microservices";
import { Cron } from "@nestjs/schedule";
import { ApiConfigService } from "src/common/api-config/api.config.service";
import { NodeService } from "src/endpoints/nodes/node.service";
import { CacheInfo } from "src/utils/cache.info";
Expand All @@ -11,10 +10,11 @@ import { TransferOwnershipExtractor } from "./extractor/transfer.ownership.extra
import { MetricsEvents } from "src/utils/metrics-events.constants";
import { LogMetricsEvent } from "src/common/entities/log.metrics.event";
import { CacheService } from "@multiversx/sdk-nestjs-cache";
import { BinaryUtils, OriginLogger } from "@multiversx/sdk-nestjs-common";
import { BinaryUtils, Lock, OriginLogger } from "@multiversx/sdk-nestjs-common";
import { PerformanceProfiler } from "@multiversx/sdk-nestjs-monitoring";
import { StakeFunction } from "src/endpoints/transactions/transaction-action/recognizers/staking/entities/stake.function";
import { ShardTransaction, TransactionProcessor } from "@multiversx/sdk-transaction-processor";
import { ExtendedCron } from "./decorators/extended-cron.decorator";

@Injectable()
export class TransactionProcessorService {
Expand All @@ -29,7 +29,8 @@ export class TransactionProcessorService {
private readonly eventEmitter: EventEmitter2,
) { }

@Cron('*/1 * * * * *')
@ExtendedCron('*/500 * * * * * *') // each 500ms
@Lock({ name: 'Transactions processor', verbose: true })
async handleNewTransactions() {
await this.transactionProcessor.start({
gatewayUrl: this.apiConfigService.getGatewayUrl(),
Expand Down