Actor picture

Compute Per Actor

mnmkng/compute-per-actor

This actor goes through your run history and calculates compute unit usage statistics per actor. This is useful to get an idea which of your actors would benefit the most from optimizations.

No credit card required

Author's avatarOndra Urban
  • Modified
  • Users5
  • Runs23

Based on the apify/actor-node-basic:v0.21.10 Docker image (see docs).

const Apify = require('apify');

const { client, utils: { log, sleep } } = Apify;

Apify.main(async () => {
    log.info('Getting actors.');
    const actors = await getActors();
    log.info('Getting actor runs.');
    const actorsWithRuns = await getActorsWithRuns(actors);
    log.info('Getting stats for individual runs.');
    const pool = createRunPool(actorsWithRuns);
    await pool.run();
    await Apify.setValue('ACTORS', actorsWithRuns);
    log.info('Calculating CU usage.');
    const results = calculateComputeUnitUsage(actorsWithRuns);
    log.info('Saving output.');
    await Apify.setValue('OUTPUT', { actors: results });
});

async function getActors() {
    const { items } = await client.acts.listActs();
    return items;
}

async function getActorsWithRuns(actors) {
    const actorsWithRunsPromises = actors.map(async (actor, actorIdx) => {
        await sleep(actorIdx * 33);
        log.info(`Getting runs for actor: ${actor.id}`);
        const { items } = await client.acts.listRuns({ actId: actor.id })
        return { ...actor, runs: items };
    }, {});
    return Promise.all(actorsWithRunsPromises);
}

async function getRunStats(actorId, run) {
    return client.acts.getRun({ actId: actorId, runId: run.id })
}

function createRunPool(actorsWithRuns) {
    const allTasks = actorsWithRuns.reduce((tasks, actor, actorIndex) => {
        const runs = actor.runs.map((run, runIndex) => ({
            actorId: actor.id,
            actorIndex,
            run,
            runIndex,
        }));
        return tasks.concat(runs);
    }, []);
    setInterval(() => {
        log.info(`There are ${allTasks.length} remaining runs to process.`);
    }, 10000)
    return new Apify.AutoscaledPool({
        minConcurrency: 5,
        runTaskFunction: async () => {
            const task = allTasks.shift();
            const runWithStats = await getRunStats(task.actorId, task.run);
            actorsWithRuns[task.actorIndex].runs[task.runIndex] = runWithStats;
        },
        isTaskReadyFunction: () => !!allTasks.length,
        isFinishedFunction: () => !allTasks.length
    })
}

function calculateComputeUnitUsage(actors) {
    return actors.map((actor) => {
        const computeUnitUsage = Array.isArray(actor.runs)
            ? actor.runs.reduce((sum, run) => sum + (run.stats.computeUnits || 0), 0)
            : null;
        return {
            id: actor.id,
            name: actor.name,
            computeUnitUsage,
        }
    })
}