Agent skill

foundatio

Use this skill when working with Foundatio infrastructure abstractions — caching, queuing, messaging, file storage, locking, or background jobs. Apply when using ICacheClient, IQueue, IMessageBus, IFileStorage, ILockProvider, or IJob, or when implementing retry/resilience patterns. Covers both in-memory and production (Redis, Elasticsearch) implementations.

Stars 2,455
Forks 508

Install this agent skill to your Project

npx add-skill https://github.com/exceptionless/Exceptionless/tree/main/.agents/skills/foundatio

SKILL.md

Foundatio

Foundatio provides pluggable infrastructure abstractions. Use context7 MCP for complete documentation.

Documentation: Use context7 to fetch current Foundatio API docs and examples.

Core Abstractions

Interface Purpose In-Memory Production
ICacheClient Distributed caching InMemoryCacheClient Redis
IQueue<T> Message queuing InMemoryQueue<T> Redis/SQS
IMessageBus Pub/sub messaging InMemoryMessageBus Redis
IFileStorage File storage InMemoryFileStorage S3/Azure
ILockProvider Distributed locking InMemoryLockProvider Redis
IResiliencePolicyProvider Retry/circuit breaker N/A Polly-based

ICacheClient

csharp
// From src/Exceptionless.Core/Services/UsageService.cs
public class UsageService
{
    private readonly ICacheClient _cache;

    public async Task<int> GetUsageAsync(string organizationId, DateTime bucketUtc)
    {
        var key = GetBucketTotalCacheKey(bucketUtc, organizationId);
        var result = await _cache.GetAsync<int>(key);
        return result.HasValue ? result.Value : 0;
    }

    public async Task IncrementUsageAsync(string organizationId, DateTime bucketUtc, int count)
    {
        var key = GetBucketTotalCacheKey(bucketUtc, organizationId);
        await _cache.IncrementAsync(key, count);

        // Track org in set for later processing
        await _cache.ListAddAsync(GetOrganizationSetKey(bucketUtc), organizationId);
    }
}

IQueue<T>

Queue items for background processing:

csharp
// Enqueue
await _queue.EnqueueAsync(new EventPost
{
    OrganizationId = orgId,
    ProjectId = projectId,
    FilePath = path
});

IMessageBus

Pub/sub for real-time notifications:

csharp
// From src/Exceptionless.Web/Hubs/MessageBusBroker.cs
await _subscriber.SubscribeAsync<EntityChanged>(OnEntityChangedAsync, shutdownToken);
await _subscriber.SubscribeAsync<PlanChanged>(OnPlanChangedAsync, shutdownToken);

// Publishing
await _messagePublisher.PublishAsync(new EntityChanged
{
    ChangeType = ChangeType.Saved,
    Type = nameof(Organization),
    Id = organization.Id
});

Jobs

QueueJobBase - Queue Processing

csharp
// From src/Exceptionless.Core/Jobs/EventPostsJob.cs
[Job(Description = "Processes queued events.", InitialDelay = "2s")]
public class EventPostsJob : QueueJobBase<EventPost>
{
    public EventPostsJob(
        IQueue<EventPost> queue,
        TimeProvider timeProvider,
        IResiliencePolicyProvider resiliencePolicyProvider,
        ILoggerFactory loggerFactory)
        : base(queue, timeProvider, resiliencePolicyProvider, loggerFactory)
    {
        AutoComplete = false;  // Manual completion after processing
    }

    protected override async Task<JobResult> ProcessQueueEntryAsync(QueueEntryContext<EventPost> context)
    {
        var entry = context.QueueEntry;
        using var _ = _logger.BeginScope(new ExceptionlessState()
            .Organization(entry.Value.OrganizationId)
            .Project(entry.Value.ProjectId));

        // Process the event...
        await entry.CompleteAsync();
        return JobResult.Success;
    }
}

IJob - Scheduled Jobs

csharp
// From src/Exceptionless.Core/Jobs/CleanupDataJob.cs
[Job(Description = "Deletes old data.", InitialDelay = "1m", Interval = "1h")]
public class CleanupDataJob : IJob
{
    public async Task<JobResult> RunAsync(CancellationToken cancellationToken = default)
    {
        await CleanupOrganizationsAsync(cancellationToken);
        await CleanupProjectsAsync(cancellationToken);
        return JobResult.Success;
    }
}

Job Attributes

csharp
[Job(
    Description = "Job description",
    InitialDelay = "2s",           // Delay before first run
    Interval = "5m",               // Run every 5 minutes
    IterationLimit = 1,            // Run once then stop
    IsContinuous = true            // Keep running
)]

Resilience with IResiliencePolicyProvider

IResiliencePolicyProvider provides retry policies for Foundatio components:

csharp
// Registration in Bootstrapper
services.AddSingleton<IResiliencePolicyProvider, ResiliencePolicyProvider>();

All queue jobs inherit resilience via base class:

csharp
// From src/Exceptionless.Core/Jobs/EventPostsJob.cs
public class EventPostsJob : QueueJobBase<EventPost>
{
    public EventPostsJob(
        IQueue<EventPost> queue,
        TimeProvider timeProvider,
        IResiliencePolicyProvider resiliencePolicyProvider,
        ILoggerFactory loggerFactory)
        : base(queue, timeProvider, resiliencePolicyProvider, loggerFactory)
    {
        AutoComplete = false;  // Manual completion for control
    }

    protected override async Task<JobResult> ProcessQueueEntryAsync(QueueEntryContext<EventPost> context)
    {
        var entry = context.QueueEntry;
        try
        {
            // Process...
            await entry.CompleteAsync();
            return JobResult.Success;
        }
        catch (Exception ex) when (ex is ValidationException or MiniValidatorException)
        {
            // Don't retry validation errors
            await entry.CompleteAsync();
            return JobResult.Success;
        }
    }
}

Components configured with resilience:

csharp
// From src/Exceptionless.Core/Bootstrapper.cs
services.AddSingleton<CacheLockProvider>(s => new CacheLockProvider(
    s.GetRequiredService<ICacheClient>(),
    s.GetRequiredService<IMessageBus>(),
    s.GetRequiredService<TimeProvider>(),
    s.GetRequiredService<IResiliencePolicyProvider>(),
    s.GetRequiredService<ILoggerFactory>()
));

Queue entries can be retried via AbandonAsync() or completed via CompleteAsync().

Repositories

Foundatio.Repositories provides Elasticsearch integration:

csharp
// From src/Exceptionless.Core/Repositories/Base/RepositoryBase.cs
public abstract class RepositoryBase<T> : ElasticRepositoryBase<T>
{
    // Automatic change notifications via IMessageBus
    protected override Task PublishChangeTypeMessageAsync(
        ChangeType changeType,
        T? document,
        IDictionary<string, object>? data = null)
    {
        return PublishMessageAsync(CreateEntityChanged(changeType, document));
    }
}

Repository options:

csharp
// Cache results
await _repository.GetByIdAsync(id, o => o.Cache());

// Immediate consistency (for tests)
await _repository.AddAsync(entity, o => o.ImmediateConsistency());

Testing

Use in-memory implementations for tests:

csharp
services.AddSingleton<ICacheClient, InMemoryCacheClient>();
services.AddSingleton<IMessageBus, InMemoryMessageBus>();
services.AddSingleton(typeof(IQueue<>), typeof(InMemoryQueue<>));

See backend-testing for ProxyTimeProvider patterns.

Resilience & Reliability

Build resilient systems that handle failures gracefully:

  • Expect failures: Network calls fail, resources exhaust, concurrent access races
  • Timeouts everywhere: Never wait indefinitely; use cancellation tokens
  • Retry with backoff: Use exponential backoff with jitter for transient failures
  • Graceful degradation: Return cached data, default values, or partial results when appropriate
  • Idempotency: Design operations to be safely retryable
  • Resource limits: Bound queues, caches, and buffers to prevent memory exhaustion

Retry Pattern

csharp
// Queue entries support automatic retry
await entry.AbandonAsync();  // Return to queue for retry
await entry.CompleteAsync(); // Mark as successfully processed

// Don't retry validation errors - they'll never succeed
catch (Exception ex) when (ex is ValidationException or MiniValidatorException)
{
    await entry.CompleteAsync();  // Don't retry
    return JobResult.Success;
}

Expand your agent's capabilities with these related and highly-rated skills.

exceptionless/Exceptionless

foundatio-repositories

2,455 508
Explore
exceptionless/Exceptionless

releasenotes

Generate formatted changelogs from git history since the last release tag. Use when preparing release notes that categorize changes into breaking changes, features, fixes, and other sections.

2,455 508
Explore
exceptionless/Exceptionless

e2e-testing

Use this skill when writing or running end-to-end browser tests with Playwright. Covers Page Object Model patterns, selector strategies (data-testid, getByRole, getByLabel), fixtures, and accessibility audits with axe-playwright. Apply when adding E2E test coverage, debugging flaky tests, or testing user flows through the browser.

2,455 508
Explore
exceptionless/Exceptionless

tanstack-query

Use this skill when fetching data, managing server state, or handling API mutations in the Svelte frontend. Covers createQuery, createMutation, query keys, cache invalidation, optimistic updates, and WebSocket-driven refetching. Apply when adding API calls, managing loading/error states, or coordinating cache updates after mutations.

2,455 508
Explore
exceptionless/Exceptionless

dogfood

Systematically explore and test a web application to find bugs, UX issues, and other problems. Use when asked to "dogfood", "QA", "exploratory test", "find issues", "bug hunt", "test this app/site/platform", or review the quality of a web application. Produces a structured report with full reproduction evidence -- step-by-step screenshots, repro videos, and detailed repro steps for every issue -- so findings can be handed directly to the responsible teams.

2,455 508
Explore
exceptionless/Exceptionless

storybook

Use this skill when creating or updating Storybook stories for Svelte components. Covers Svelte CSF story format, defineMeta, argTypes, snippet-based customization, and autodocs. Apply when adding visual documentation for components, setting up story files, or running Storybook for development.

2,455 508
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results