
foundatio
by exceptionless
Exceptionless application
SKILL.md
name: Foundatio description: | Foundatio infrastructure abstractions for caching, queuing, messaging, file storage, locking, jobs, and resilience. Use context7 for complete API documentation. Keywords: Foundatio, ICacheClient, IQueue, IMessageBus, IFileStorage, ILockProvider, IJob, QueueJobBase, resilience, retry, Redis, Elasticsearch
Foundatio
Foundatio provides pluggable infrastructure abstractions. Use context7 MCP for complete documentation.
Documentation: Use
context7to 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
// 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
Queue items for background processing:
// Enqueue
await _queue.EnqueueAsync(new EventPost
{
OrganizationId = orgId,
ProjectId = projectId,
FilePath = path
});
IMessageBus
Pub/sub for real-time notifications:
// 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
// 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
// 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
[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:
// Registration in Bootstrapper
services.AddSingleton<IResiliencePolicyProvider, ResiliencePolicyProvider>();
All queue jobs inherit resilience via base class:
// 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:
// 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:
// 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:
// 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:
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
// 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;
}
Score
Total Score
Based on repository quality metrics
SKILL.mdファイルが含まれている
ライセンスが設定されている
100文字以上の説明がある
GitHub Stars 1000以上
1ヶ月以内に更新
10回以上フォークされている
オープンIssueが50未満
プログラミング言語が設定されている
1つ以上のタグが設定されている
Reviews
Reviews coming soon


