Pre-release: sf-bedrock is under active development. APIs and docs may change before general availability.
Bedrock Docs
Repository
Tools

Limiter

Limiter reports current transaction and org/platform limit usage in one place. Use it in subscriber Apex before starting work that should wait when the org is near a limit.

Overview

Limiter gives subscriber code a single place to read limit usage and answer one small question: is this specific limit still below the threshold I care about? It covers transaction limits from Limits and org/platform limits from OrgLimits.

Use Limiter when subscriber Apex needs to decide whether to start more work. Common cases include queueable launch checks, platform event publish checks, API-sensitive work, and any place where subscriber settings own the threshold.

Reach for Salesforce Limits or OrgLimits directly when you only need one raw counter in a private local method and do not need a shared Bedrock dependency or a mockable seam.

Quickstart

Check a known limit against a subscriber-provided threshold:

Decimal thresholdPercent = 80;

if (!Limiter.isSafe(Limiter.types.DAILY_ASYNC_APEX_EXECUTIONS, thresholdPercent)) {
    return;
}

System.enqueueJob(new RebuildSearchIndexJob());

Read a specific limit when you need the numbers:

Limiter.LimitUsage usage = Limiter.getLimit(Limiter.types.QUEUEABLE_JOBS);

if (usage != null) {
    System.debug('Queueable jobs used: ' + usage.used + ' of ' + usage.allowed);
}

Examples

Gate queueable work

Use isSafe(type, thresholdPercent) when subscriber code owns the setting and only needs a yes/no answer.

public inherited sharing class SubscriberWorkLauncher {
    public static void launch() {
        Decimal thresholdPercent = 85;

        if (
            !Limiter.isSafe(
                Limiter.types.DAILY_ASYNC_APEX_EXECUTIONS,
                thresholdPercent
            )
        ) {
            return;
        }

        System.enqueueJob(new ProcessPendingWorkJob());
    }
}

Inspect one limit

getLimit(type) returns a Limiter.LimitUsage object with the limit name, current usage, allowed amount, remaining capacity, and usage ratio.

Limiter.LimitUsage usage = Limiter.getLimit(Limiter.types.DAILY_STANDARD_VOLUME_PLATFORM_EVENTS);

if (usage != null && usage.remaining > 0) {
    System.debug('Platform event publishes remaining: ' + usage.remaining);
}

Read all available limits

getLimits() returns a map keyed by limit name. The map includes Bedrock’s known transaction limit names and the names returned by Salesforce OrgLimits.

Map<String, Limiter.LimitUsage> limitsByName = Limiter.getLimits();

for (String name : limitsByName.keySet()) {
    Limiter.LimitUsage usage = limitsByName.get(name);
    System.debug(name + ': ' + usage.ratio);
}

Use a raw OrgLimits key

The enum covers known Bedrock-friendly names. If Salesforce exposes an OrgLimits key that is not represented in Limiter.types, use the string overload.

if (!Limiter.isSafe('DailyApiRequests', 90)) {
    return;
}

Known Limit Types

Use Limiter.types when you do not want magic strings in subscriber code.

TypeSalesforce key
QUEUEABLE_JOBSQueueableJobs
SOQL_QUERIESSOQLQueries
CPU_TIMECPUTime
HEAP_SIZEHeapSize
DML_STATEMENTSDMLStatements
DAILY_ASYNC_APEX_EXECUTIONSDailyAsyncApexExecutions
DAILY_STANDARD_VOLUME_PLATFORM_EVENTSDailyStandardVolumePlatformEvents

Testing

Use LimiterMock to seed only the limits a test cares about. The mock extends Limiter.Service, so tests can install it with the @testVisible setMock hook.

@istest static void launch_skipsWorkWhenDailyAsyncIsAtThreshold() {
    Limiter.setMock(new LimiterMock()
        .setLimit(Limiter.types.DAILY_ASYNC_APEX_EXECUTIONS, 90, 100));

    Boolean isSafe = Limiter.isSafe(Limiter.types.DAILY_ASYNC_APEX_EXECUTIONS);

    Assert.isFalse(isSafe,
        'Expected daily async usage at the default threshold to be unsafe.');
}

The mock also accepts a raw limit name when a test needs a Salesforce key that is not in Limiter.types.

@istest static void apiGate_usesRawOrgLimitName() {
    Limiter.setMock(new LimiterMock()
        .setLimit('DailyApiRequests', 75, 100));

    Assert.isTrue(Limiter.isSafe('DailyApiRequests', 80),
        'Expected API usage below the configured threshold to be safe.');
}

How It Works

Three ideas explain everything Limiter does.

1. Gather transaction limits

Limiter.Service.getLimits() adds transaction usage for queueable jobs, SOQL queries, CPU time, heap size, and DML statements by reading the Salesforce Limits class.

2. Add Salesforce org/platform limits

The same method loops over OrgLimits.getMap().values() and adds every Salesforce-provided org limit to the result map. This is how callers can inspect daily async, platform event, API, storage, and other org-level counters without Limiter hard-coding every possible limit key.

3. Compare one limit to one threshold

isSafe(name, thresholdPercent) reads one limit and compares its ratio to the threshold. The threshold is a percent, so 80 means “unsafe at 80 percent or higher.” Subscriber code owns threshold configuration and passes the value in.

Limiter intentionally checks one named limit at a time. It reports capacity; it does not decide which limits matter to a subscriber’s own service.

Public API

Limiter is declared public virtual inherited sharing. It exposes static facade methods backed by an injectable nested Service.

A note on access modifiers: Apex members with no access modifier are private. The singleton instance, the add(...) helper, and the @testVisible setMock(...) hook are intentionally not public API.

Static Methods

MethodSignatureReturnsDescription
getLimitsgetLimits()Map<String, Limiter.LimitUsage>Returns all tracked transaction and org/platform limits keyed by limit name.
getLimitgetLimit(String name)Limiter.LimitUsageReturns one tracked limit by raw name, or null when the name is not present.
getLimitgetLimit(Limiter.types name)Limiter.LimitUsageReturns one tracked known limit by enum value.
isSafeisSafe(String name)BooleanChecks one raw named limit against the default 90 percent threshold.
isSafeisSafe(String name, Decimal thresholdPercent)BooleanChecks one raw named limit against the caller-provided threshold.
isSafeisSafe(Limiter.types name)BooleanChecks one known limit against the default 90 percent threshold.
isSafeisSafe(Limiter.types name, Decimal thresholdPercent)BooleanChecks one known limit against the caller-provided threshold.
keykey(Limiter.types name)StringReturns the Salesforce key used for a known enum value.

Inner Types

TypeMembersDescription
Limiter.LimitUsagename, used, allowed, remaining, ratioA single limit’s current usage. ratio is used / allowed, or 0 when allowed is blank or zero.
Limiter.typesQUEUEABLE_JOBS, SOQL_QUERIES, CPU_TIME, HEAP_SIZE, DML_STATEMENTS, DAILY_ASYNC_APEX_EXECUTIONS, DAILY_STANDARD_VOLUME_PLATFORM_EVENTSKnown limit keys with enum overloads for getLimit and isSafe.
Limiter.ServicegetLimits, getLimit, isSafeInjectable implementation used by the static facade.

Test Helper

ClassMethodDescription
LimiterMocksetLimit(String name, Integer used, Integer allowed)Seeds a raw named limit for tests.
LimiterMocksetLimit(Limiter.types name, Integer used, Integer allowed)Seeds a known enum limit for tests.
LimiterMocklimits(Map<String, Limiter.LimitUsage> currentLimits)Replaces the mock’s full limits map.

Notes & Edge Cases

  • Thresholds are subscriber-owned. Limiter has a 90 percent default for the no-threshold overloads, but subscriber settings should pass an explicit threshold when the decision is configurable.

  • Missing names are unsafe for checks. getLimit(name) returns null when a name is not present. isSafe(name, thresholdPercent) returns false for a missing name so bad call sites fail closed.

  • Limiter.types is not exhaustive. Salesforce can expose more OrgLimits names than the enum currently models. Use the string overload for those names.

  • The ratio is a decimal from 0 to 1. A 75 percent used limit has ratio = 0.75. The threshold argument is a percent, so pass 75, not 0.75.

  • Limiter only reports and checks usage. Subscriber code decides what to do when a limit is unsafe.