export function idempotent(doIt, keyBy = (...args) => args.join(";")) {
    const executedKeys = new Set();
    return async (...args) => {
        const key = keyBy(...args);
        if (executedKeys.has(key)) {
            return;
        }
        await doIt(...args);
        executedKeys.add(key);
    };
}
export class QueueRepo {
    constructor(redis, jobsRepo) {
        this.redis = redis;
        this.jobsRepo = jobsRepo;
        this.isMigrated = process.env.SKIP_QUEUE_REPO_MIGRATION === "true";
        this.add = idempotent(async (endpoint, tokenId) => {
            await this.redis.sadd(`queues:by-token:${tokenId}`, endpoint);
        });
    }
    async get(tokenId) {
        await this.ensureMigrated();
        return await this.redis.smembers(`queues:by-token:${tokenId}`);
    }
    async ensureMigrated() {
        if (!this.isMigrated) {
            this.isMigrated = (await this.redis.exists(`queues-migrated`)) === 1;
        }
        if (this.isMigrated) {
            return;
        }
        const tokenIdToEndpoints = {};
        let cursor = 0;
        do {
            const result = await this.jobsRepo.findAll({
                count: 1000,
                cursor,
            });
            cursor = result.cursor;
            result.jobs.forEach(({ endpoint, tokenId }) => {
                if (!tokenIdToEndpoints[tokenId]) {
                    tokenIdToEndpoints[tokenId] = new Set();
                }
                tokenIdToEndpoints[tokenId].add(endpoint);
            });
        } while (cursor !== 0);
        const pipeline = this.redis.pipeline();
        for (const [tokenId, endpoints] of Object.entries(tokenIdToEndpoints)) {
            pipeline.sadd(`queues:by-token:${tokenId}`, ...endpoints);
        }
        pipeline.set(`queues-migrated`, "ture");
        await pipeline.exec();
    }
}
//# sourceMappingURL=queue-repo.js.map