Initial push
This commit is contained in:
33
samples/BestPractices/BestPractices.csproj
Normal file
33
samples/BestPractices/BestPractices.csproj
Normal file
@@ -0,0 +1,33 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.8.16" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Core\SqrtSpace.SpaceTime.Core.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.AspNetCore\SqrtSpace.SpaceTime.AspNetCore.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Linq\SqrtSpace.SpaceTime.Linq.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Collections\SqrtSpace.SpaceTime.Collections.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.EntityFramework\SqrtSpace.SpaceTime.EntityFramework.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Caching\SqrtSpace.SpaceTime.Caching.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Diagnostics\SqrtSpace.SpaceTime.Diagnostics.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.MemoryManagement\SqrtSpace.SpaceTime.MemoryManagement.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Pipeline\SqrtSpace.SpaceTime.Pipeline.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Distributed\SqrtSpace.SpaceTime.Distributed.csproj" />
|
||||
<ProjectReference Include="..\..\src\SqrtSpace.SpaceTime.Scheduling\SqrtSpace.SpaceTime.Scheduling.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
486
samples/BestPractices/Program.cs
Normal file
486
samples/BestPractices/Program.cs
Normal file
@@ -0,0 +1,486 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SqrtSpace.SpaceTime.AspNetCore;
|
||||
using SqrtSpace.SpaceTime.Caching;
|
||||
using SqrtSpace.SpaceTime.Configuration;
|
||||
using SqrtSpace.SpaceTime.Core;
|
||||
using SqrtSpace.SpaceTime.Diagnostics;
|
||||
using SqrtSpace.SpaceTime.Distributed;
|
||||
using SqrtSpace.SpaceTime.EntityFramework;
|
||||
using SqrtSpace.SpaceTime.Linq;
|
||||
using SqrtSpace.SpaceTime.MemoryManagement;
|
||||
using SqrtSpace.SpaceTime.Pipeline;
|
||||
using SqrtSpace.SpaceTime.Scheduling;
|
||||
using System.Reactive.Linq;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Configure all SpaceTime services with best practices
|
||||
var spaceTimeConfig = new SpaceTimeConfiguration();
|
||||
builder.Configuration.GetSection("SpaceTime").Bind(spaceTimeConfig);
|
||||
builder.Services.AddSingleton(spaceTimeConfig);
|
||||
|
||||
// Configure memory limits based on environment
|
||||
builder.Services.Configure<SpaceTimeConfiguration>(options =>
|
||||
{
|
||||
var environment = builder.Environment;
|
||||
|
||||
// Set memory limits based on deployment environment
|
||||
options.Memory.MaxMemory = environment.IsDevelopment()
|
||||
? 256 * 1024 * 1024 // 256MB for dev
|
||||
: 1024 * 1024 * 1024; // 1GB for production
|
||||
|
||||
// Enable adaptive features
|
||||
options.Algorithms.EnableAdaptiveSelection = true;
|
||||
options.Features.EnableAdaptiveDataStructures = true;
|
||||
|
||||
// Configure based on container limits if available
|
||||
var memoryLimit = Environment.GetEnvironmentVariable("MEMORY_LIMIT");
|
||||
if (long.TryParse(memoryLimit, out var limit))
|
||||
{
|
||||
options.Memory.MaxMemory = (long)(limit * 0.8); // Use 80% of container limit
|
||||
}
|
||||
});
|
||||
|
||||
// Add all SpaceTime services
|
||||
builder.Services.AddSpaceTime(options =>
|
||||
{
|
||||
options.EnableCheckpointing = true;
|
||||
options.EnableStreaming = true;
|
||||
});
|
||||
|
||||
// Add caching with proper configuration
|
||||
builder.Services.AddSpaceTimeCaching();
|
||||
builder.Services.AddSpaceTimeCache<string, object>("main", options =>
|
||||
{
|
||||
options.MaxHotCacheSize = 50 * 1024 * 1024; // 50MB hot cache
|
||||
options.Strategy = MemoryStrategy.SqrtN;
|
||||
});
|
||||
|
||||
// Add distributed processing if Redis is available
|
||||
var redisConnection = builder.Configuration.GetConnectionString("Redis");
|
||||
if (!string.IsNullOrEmpty(redisConnection))
|
||||
{
|
||||
// Add Redis services manually
|
||||
builder.Services.AddSingleton<StackExchange.Redis.IConnectionMultiplexer>(sp =>
|
||||
StackExchange.Redis.ConnectionMultiplexer.Connect(redisConnection));
|
||||
builder.Services.AddSingleton<ISpaceTimeCoordinator, SpaceTimeCoordinator>();
|
||||
}
|
||||
|
||||
// Add diagnostics
|
||||
builder.Services.AddSingleton<ISpaceTimeDiagnostics, SpaceTimeDiagnostics>();
|
||||
|
||||
// Add memory management
|
||||
builder.Services.AddSingleton<IMemoryPressureMonitor, MemoryPressureMonitor>();
|
||||
|
||||
// Add pipeline support
|
||||
builder.Services.AddSingleton<IPipelineFactory, PipelineFactory>();
|
||||
|
||||
// Add Entity Framework with SpaceTime optimizations
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
|
||||
.UseSpaceTimeOptimizer(opt =>
|
||||
{
|
||||
opt.EnableSqrtNChangeTracking = true;
|
||||
opt.BufferPoolStrategy = BufferPoolStrategy.SqrtN;
|
||||
});
|
||||
});
|
||||
|
||||
// Add controllers and other services
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
// Register application services
|
||||
builder.Services.AddScoped<IOrderService, OrderService>();
|
||||
builder.Services.AddHostedService<DataProcessingBackgroundService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
// Add SpaceTime middleware
|
||||
app.UseSpaceTime();
|
||||
|
||||
app.UseAuthorization();
|
||||
app.MapControllers();
|
||||
|
||||
// Map health check endpoint
|
||||
app.MapGet("/health", async (IMemoryPressureMonitor monitor) =>
|
||||
{
|
||||
var stats = monitor.CurrentStatistics;
|
||||
return Results.Ok(new
|
||||
{
|
||||
Status = "Healthy",
|
||||
MemoryPressure = monitor.CurrentPressureLevel.ToString(),
|
||||
MemoryUsage = new
|
||||
{
|
||||
ManagedMemoryMB = stats.ManagedMemory / (1024.0 * 1024.0),
|
||||
WorkingSetMB = stats.WorkingSet / (1024.0 * 1024.0),
|
||||
AvailablePhysicalMemoryMB = stats.AvailablePhysicalMemory / (1024.0 * 1024.0)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
// Application services demonstrating best practices
|
||||
|
||||
public interface IOrderService
|
||||
{
|
||||
Task<IEnumerable<Order>> GetLargeOrderSetAsync(OrderFilter filter);
|
||||
Task<OrderProcessingResult> ProcessOrderBatchAsync(IEnumerable<Order> orders);
|
||||
}
|
||||
|
||||
public class OrderService : IOrderService
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly ICacheManager _cacheManager;
|
||||
private readonly ISpaceTimeDiagnostics _diagnostics;
|
||||
private readonly IPipelineFactory _pipelineFactory;
|
||||
private readonly ILogger<OrderService> _logger;
|
||||
|
||||
public OrderService(
|
||||
ApplicationDbContext context,
|
||||
ICacheManager cacheManager,
|
||||
ISpaceTimeDiagnostics diagnostics,
|
||||
IPipelineFactory pipelineFactory,
|
||||
ILogger<OrderService> logger)
|
||||
{
|
||||
_context = context;
|
||||
_cacheManager = cacheManager;
|
||||
_diagnostics = diagnostics;
|
||||
_pipelineFactory = pipelineFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Order>> GetLargeOrderSetAsync(OrderFilter filter)
|
||||
{
|
||||
using var operation = _diagnostics.StartOperation("GetLargeOrderSet", OperationType.Custom);
|
||||
|
||||
try
|
||||
{
|
||||
// Use SpaceTime LINQ for memory-efficient query
|
||||
var query = _context.Orders
|
||||
.Where(o => o.CreatedDate >= filter.StartDate && o.CreatedDate <= filter.EndDate);
|
||||
|
||||
if (!string.IsNullOrEmpty(filter.Status))
|
||||
query = query.Where(o => o.Status == filter.Status);
|
||||
|
||||
// Use standard LINQ for now
|
||||
var orders = await query
|
||||
.OrderBy(o => o.CreatedDate)
|
||||
.ToListAsync();
|
||||
|
||||
operation.AddTag("order.count", orders.Count);
|
||||
return orders;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
operation.AddTag("error", ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<OrderProcessingResult> ProcessOrderBatchAsync(IEnumerable<Order> orders)
|
||||
{
|
||||
var processedCount = 0;
|
||||
var startTime = DateTime.UtcNow;
|
||||
var errors = new List<Exception>();
|
||||
|
||||
try
|
||||
{
|
||||
// Simple processing without complex pipeline for now
|
||||
var orderList = orders.ToList();
|
||||
|
||||
// Validate orders
|
||||
foreach (var order in orderList)
|
||||
{
|
||||
if (order.TotalAmount <= 0)
|
||||
throw new ValidationException($"Invalid order amount: {order.Id}");
|
||||
}
|
||||
|
||||
// Batch load customer data
|
||||
var customerIds = orderList.Select(o => o.CustomerId).Distinct();
|
||||
var customers = await _context.Customers
|
||||
.Where(c => customerIds.Contains(c.Id))
|
||||
.ToDictionaryAsync(c => c.Id);
|
||||
|
||||
// Process orders in parallel
|
||||
var tasks = orderList.Select(async order =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var customer = customers.GetValueOrDefault(order.CustomerId);
|
||||
var enriched = new EnrichedOrder { Order = order, Customer = customer };
|
||||
var tax = await CalculateTaxAsync(enriched);
|
||||
|
||||
var processed = new ProcessedOrder
|
||||
{
|
||||
Id = order.Id,
|
||||
CustomerId = order.CustomerId,
|
||||
TotalAmount = order.TotalAmount,
|
||||
TotalWithTax = order.TotalAmount + tax,
|
||||
ProcessedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
Interlocked.Increment(ref processedCount);
|
||||
return processed;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add(ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error processing order batch");
|
||||
errors.Add(ex);
|
||||
}
|
||||
|
||||
return new OrderProcessingResult
|
||||
{
|
||||
ProcessedCount = processedCount,
|
||||
Duration = DateTime.UtcNow - startTime,
|
||||
Success = errors.Count == 0
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<decimal> CalculateTaxAsync(EnrichedOrder order)
|
||||
{
|
||||
// Simulate tax calculation
|
||||
await Task.Delay(10);
|
||||
return order.Order.TotalAmount * 0.08m; // 8% tax
|
||||
}
|
||||
}
|
||||
|
||||
// Background service demonstrating memory-aware processing
|
||||
public class DataProcessingBackgroundService : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IMemoryPressureMonitor _memoryMonitor;
|
||||
private readonly TaskScheduler _scheduler;
|
||||
private readonly ILogger<DataProcessingBackgroundService> _logger;
|
||||
|
||||
public DataProcessingBackgroundService(
|
||||
IServiceProvider serviceProvider,
|
||||
IMemoryPressureMonitor memoryMonitor,
|
||||
TaskScheduler scheduler,
|
||||
ILogger<DataProcessingBackgroundService> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_memoryMonitor = memoryMonitor;
|
||||
_scheduler = scheduler;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
// Subscribe to memory pressure events
|
||||
_memoryMonitor.PressureEvents
|
||||
.Where(e => e.CurrentLevel >= SqrtSpace.SpaceTime.MemoryManagement.MemoryPressureLevel.High)
|
||||
.Subscribe(e =>
|
||||
{
|
||||
_logger.LogWarning("High memory pressure detected, pausing processing");
|
||||
// Implement backpressure
|
||||
});
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Schedule work based on memory availability
|
||||
await Task.Factory.StartNew(
|
||||
async () => await ProcessNextBatchAsync(stoppingToken),
|
||||
stoppingToken,
|
||||
TaskCreationOptions.None,
|
||||
_scheduler).Unwrap();
|
||||
|
||||
// Wait before next iteration
|
||||
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error in background processing");
|
||||
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessNextBatchAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
|
||||
// Get unprocessed orders in memory-efficient batches
|
||||
await foreach (var batch in context.Orders
|
||||
.Where(o => o.Status == "Pending")
|
||||
.BatchBySqrtNAsync())
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
_logger.LogInformation("Processing batch of {Count} orders", batch.Count);
|
||||
|
||||
// Process batch
|
||||
foreach (var order in batch)
|
||||
{
|
||||
order.Status = "Processed";
|
||||
order.ProcessedDate = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Controller demonstrating SpaceTime features
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class OrdersController : ControllerBase
|
||||
{
|
||||
private readonly IOrderService _orderService;
|
||||
private readonly ISpaceTimeCoordinator _coordinator;
|
||||
private readonly ILogger<OrdersController> _logger;
|
||||
|
||||
public OrdersController(
|
||||
IOrderService orderService,
|
||||
ISpaceTimeCoordinator coordinator,
|
||||
ILogger<OrdersController> logger)
|
||||
{
|
||||
_orderService = orderService;
|
||||
_coordinator = coordinator;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet("export")]
|
||||
[SpaceTimeStreaming(ChunkStrategy = ChunkStrategy.SqrtN)]
|
||||
public async IAsyncEnumerable<OrderExportDto> ExportOrders([FromQuery] OrderFilter filter)
|
||||
{
|
||||
var orders = await _orderService.GetLargeOrderSetAsync(filter);
|
||||
|
||||
await foreach (var batch in orders.BatchBySqrtNAsync())
|
||||
{
|
||||
foreach (var order in batch)
|
||||
{
|
||||
yield return new OrderExportDto
|
||||
{
|
||||
Id = order.Id,
|
||||
CustomerName = order.CustomerName,
|
||||
TotalAmount = order.TotalAmount,
|
||||
Status = order.Status,
|
||||
CreatedDate = order.CreatedDate
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("process-distributed")]
|
||||
[HttpPost("process-distributed")]
|
||||
public async Task<IActionResult> ProcessDistributed([FromBody] ProcessRequest request)
|
||||
{
|
||||
// For now, process without distributed coordination
|
||||
// TODO: Implement proper distributed processing when coordinator API is finalized
|
||||
var filter = new OrderFilter
|
||||
{
|
||||
StartDate = DateTime.UtcNow.AddDays(-30),
|
||||
EndDate = DateTime.UtcNow
|
||||
};
|
||||
|
||||
var orders = await _orderService.GetLargeOrderSetAsync(filter);
|
||||
var result = await _orderService.ProcessOrderBatchAsync(orders);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Data models
|
||||
public class ApplicationDbContext : DbContext
|
||||
{
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
public DbSet<Customer> Customers { get; set; }
|
||||
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class Order
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string CustomerId { get; set; } = "";
|
||||
public string CustomerName { get; set; } = "";
|
||||
public decimal TotalAmount { get; set; }
|
||||
public string Status { get; set; } = "";
|
||||
public DateTime CreatedDate { get; set; }
|
||||
public DateTime? ProcessedDate { get; set; }
|
||||
}
|
||||
|
||||
public class Customer
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string Name { get; set; } = "";
|
||||
public string Email { get; set; } = "";
|
||||
}
|
||||
|
||||
public class OrderFilter
|
||||
{
|
||||
public DateTime StartDate { get; set; }
|
||||
public DateTime EndDate { get; set; }
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
|
||||
public class OrderExportDto
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string CustomerName { get; set; } = "";
|
||||
public decimal TotalAmount { get; set; }
|
||||
public string Status { get; set; } = "";
|
||||
public DateTime CreatedDate { get; set; }
|
||||
}
|
||||
|
||||
public class ProcessRequest
|
||||
{
|
||||
public string WorkloadId { get; set; } = "";
|
||||
public long EstimatedSize { get; set; }
|
||||
}
|
||||
|
||||
public class OrderProcessingResult
|
||||
{
|
||||
public int ProcessedCount { get; set; }
|
||||
public TimeSpan Duration { get; set; }
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
|
||||
public class EnrichedOrder
|
||||
{
|
||||
public Order Order { get; set; } = null!;
|
||||
public Customer? Customer { get; set; }
|
||||
}
|
||||
|
||||
public class ProcessedOrder
|
||||
{
|
||||
public string Id { get; set; } = "";
|
||||
public string CustomerId { get; set; } = "";
|
||||
public decimal TotalAmount { get; set; }
|
||||
public decimal TotalWithTax { get; set; }
|
||||
public DateTime ProcessedAt { get; set; }
|
||||
}
|
||||
|
||||
public class ValidationException : Exception
|
||||
{
|
||||
public ValidationException(string message) : base(message) { }
|
||||
}
|
||||
12
samples/BestPractices/Properties/launchSettings.json
Normal file
12
samples/BestPractices/Properties/launchSettings.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"profiles": {
|
||||
"BestPractices": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:50879;http://localhost:50880"
|
||||
}
|
||||
}
|
||||
}
|
||||
328
samples/BestPractices/README.md
Normal file
328
samples/BestPractices/README.md
Normal file
@@ -0,0 +1,328 @@
|
||||
# SqrtSpace SpaceTime Best Practices
|
||||
|
||||
This project demonstrates best practices for building production-ready applications using the SqrtSpace SpaceTime library. It showcases advanced patterns and configurations for optimal memory efficiency and performance.
|
||||
|
||||
## Key Concepts Demonstrated
|
||||
|
||||
### 1. **Comprehensive Service Configuration**
|
||||
|
||||
The application demonstrates proper configuration of all SpaceTime services:
|
||||
|
||||
```csharp
|
||||
// Environment-aware memory configuration
|
||||
builder.Services.Configure<SpaceTimeConfiguration>(options =>
|
||||
{
|
||||
options.Memory.MaxMemory = environment.IsDevelopment()
|
||||
? 256 * 1024 * 1024 // 256MB for dev
|
||||
: 1024 * 1024 * 1024; // 1GB for production
|
||||
|
||||
// Respect container limits
|
||||
var memoryLimit = Environment.GetEnvironmentVariable("MEMORY_LIMIT");
|
||||
if (long.TryParse(memoryLimit, out var limit))
|
||||
{
|
||||
options.Memory.MaxMemory = (long)(limit * 0.8); // Use 80% of container limit
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 2. **Layered Caching Strategy**
|
||||
|
||||
Implements hot/cold tiered caching with automatic spill-to-disk:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddSpaceTimeCaching(options =>
|
||||
{
|
||||
options.MaxHotMemory = 50 * 1024 * 1024; // 50MB hot cache
|
||||
options.EnableColdStorage = true;
|
||||
options.ColdStoragePath = Path.Combine(Path.GetTempPath(), "spacetime-cache");
|
||||
});
|
||||
```
|
||||
|
||||
### 3. **Production-Ready Diagnostics**
|
||||
|
||||
Comprehensive monitoring with OpenTelemetry integration:
|
||||
|
||||
```csharp
|
||||
builder.Services.AddSpaceTimeDiagnostics(options =>
|
||||
{
|
||||
options.EnableMetrics = true;
|
||||
options.EnableTracing = true;
|
||||
options.SamplingRate = builder.Environment.IsDevelopment() ? 1.0 : 0.1;
|
||||
});
|
||||
```
|
||||
|
||||
### 4. **Entity Framework Integration**
|
||||
|
||||
Shows how to configure EF Core with SpaceTime optimizations:
|
||||
|
||||
```csharp
|
||||
options.UseSqlServer(connectionString)
|
||||
.UseSpaceTimeOptimizer(opt =>
|
||||
{
|
||||
opt.EnableSqrtNChangeTracking = true;
|
||||
opt.BufferPoolStrategy = BufferPoolStrategy.SqrtN;
|
||||
});
|
||||
```
|
||||
|
||||
### 5. **Memory-Aware Background Processing**
|
||||
|
||||
Background services that respond to memory pressure:
|
||||
|
||||
```csharp
|
||||
_memoryMonitor.PressureEvents
|
||||
.Where(e => e.CurrentLevel >= MemoryPressureLevel.High)
|
||||
.Subscribe(e =>
|
||||
{
|
||||
_logger.LogWarning("High memory pressure detected, pausing processing");
|
||||
// Implement backpressure
|
||||
});
|
||||
```
|
||||
|
||||
### 6. **Pipeline Pattern for Complex Processing**
|
||||
|
||||
Multi-stage processing with checkpointing:
|
||||
|
||||
```csharp
|
||||
var pipeline = _pipelineFactory.CreatePipeline<Order, ProcessedOrder>("OrderProcessing")
|
||||
.Configure(config =>
|
||||
{
|
||||
config.ExpectedItemCount = orders.Count();
|
||||
config.EnableCheckpointing = true;
|
||||
})
|
||||
.AddTransform("Validate", ValidateOrder)
|
||||
.AddBatch("EnrichCustomerData", EnrichWithCustomerData)
|
||||
.AddParallel("CalculateTax", CalculateTax, maxConcurrency: 4)
|
||||
.AddCheckpoint("SaveProgress")
|
||||
.Build();
|
||||
```
|
||||
|
||||
### 7. **Distributed Processing Coordination**
|
||||
|
||||
Shows how to partition work across multiple nodes:
|
||||
|
||||
```csharp
|
||||
var partition = await _coordinator.RequestPartitionAsync(
|
||||
request.WorkloadId,
|
||||
request.EstimatedSize);
|
||||
|
||||
// Process only this node's portion
|
||||
var filter = new OrderFilter
|
||||
{
|
||||
StartDate = partition.StartRange,
|
||||
EndDate = partition.EndRange
|
||||
};
|
||||
```
|
||||
|
||||
### 8. **Streaming API Endpoints**
|
||||
|
||||
Demonstrates memory-efficient streaming with automatic chunking:
|
||||
|
||||
```csharp
|
||||
[HttpGet("export")]
|
||||
[SpaceTimeStreaming(ChunkStrategy = ChunkStrategy.SqrtN)]
|
||||
public async IAsyncEnumerable<OrderExportDto> ExportOrders([FromQuery] OrderFilter filter)
|
||||
{
|
||||
await foreach (var batch in orders.BatchBySqrtNAsync())
|
||||
{
|
||||
foreach (var order in batch)
|
||||
{
|
||||
yield return MapToDto(order);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Service Layer Pattern
|
||||
|
||||
The `OrderService` demonstrates:
|
||||
- Dependency injection of SpaceTime services
|
||||
- Operation tracking with diagnostics
|
||||
- External sorting for large datasets
|
||||
- Proper error handling and logging
|
||||
|
||||
### Memory-Aware Queries
|
||||
|
||||
```csharp
|
||||
// Automatically switches to external sorting for large results
|
||||
var orders = await query
|
||||
.OrderByExternal(o => o.CreatedDate)
|
||||
.ToListWithSqrtNMemoryAsync();
|
||||
```
|
||||
|
||||
### Batch Processing
|
||||
|
||||
```csharp
|
||||
// Process data in memory-efficient batches
|
||||
await foreach (var batch in context.Orders
|
||||
.Where(o => o.Status == "Pending")
|
||||
.BatchBySqrtNAsync())
|
||||
{
|
||||
// Process batch
|
||||
}
|
||||
```
|
||||
|
||||
### Task Scheduling
|
||||
|
||||
```csharp
|
||||
// Schedule work based on memory availability
|
||||
await _scheduler.ScheduleAsync(
|
||||
async () => await ProcessNextBatchAsync(stoppingToken),
|
||||
estimatedMemory: 50 * 1024 * 1024, // 50MB
|
||||
priority: TaskPriority.Low);
|
||||
```
|
||||
|
||||
## Configuration Best Practices
|
||||
|
||||
### 1. **Environment-Based Configuration**
|
||||
|
||||
- Development: Lower memory limits, full diagnostics
|
||||
- Production: Higher limits, sampled diagnostics
|
||||
- Container: Respect container memory limits
|
||||
|
||||
### 2. **Conditional Service Registration**
|
||||
|
||||
```csharp
|
||||
// Only add distributed coordination if Redis is available
|
||||
var redisConnection = builder.Configuration.GetConnectionString("Redis");
|
||||
if (!string.IsNullOrEmpty(redisConnection))
|
||||
{
|
||||
builder.Services.AddSpaceTimeDistributed(options =>
|
||||
{
|
||||
options.NodeId = Environment.MachineName;
|
||||
options.CoordinationEndpoint = redisConnection;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **Health Monitoring**
|
||||
|
||||
```csharp
|
||||
app.MapGet("/health", async (IMemoryPressureMonitor monitor) =>
|
||||
{
|
||||
var stats = monitor.CurrentStatistics;
|
||||
return Results.Ok(new
|
||||
{
|
||||
Status = "Healthy",
|
||||
MemoryPressure = monitor.CurrentPressureLevel.ToString(),
|
||||
MemoryUsage = new
|
||||
{
|
||||
ManagedMemoryMB = stats.ManagedMemory / (1024.0 * 1024.0),
|
||||
WorkingSetMB = stats.WorkingSet / (1024.0 * 1024.0),
|
||||
AvailablePhysicalMemoryMB = stats.AvailablePhysicalMemory / (1024.0 * 1024.0)
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Production Considerations
|
||||
|
||||
### 1. **Memory Limits**
|
||||
|
||||
Always configure memory limits based on your deployment environment:
|
||||
- Container deployments: Use 80% of container limit
|
||||
- VMs: Consider other processes running
|
||||
- Serverless: Respect function memory limits
|
||||
|
||||
### 2. **Checkpointing Strategy**
|
||||
|
||||
Enable checkpointing for:
|
||||
- Long-running operations
|
||||
- Operations that process large datasets
|
||||
- Critical business processes that must be resumable
|
||||
|
||||
### 3. **Monitoring and Alerting**
|
||||
|
||||
Monitor these key metrics:
|
||||
- Memory pressure levels
|
||||
- External sort operations
|
||||
- Checkpoint frequency
|
||||
- Cache hit rates
|
||||
- Pipeline processing times
|
||||
|
||||
### 4. **Error Handling**
|
||||
|
||||
Implement proper error handling:
|
||||
- Use diagnostics to track operations
|
||||
- Log errors with context
|
||||
- Implement retry logic for transient failures
|
||||
- Clean up resources on failure
|
||||
|
||||
### 5. **Performance Tuning**
|
||||
|
||||
- Adjust batch sizes based on workload
|
||||
- Configure parallelism based on CPU cores
|
||||
- Set appropriate cache sizes
|
||||
- Monitor and adjust memory thresholds
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
### 1. **Load Testing**
|
||||
|
||||
Test with datasets that exceed memory limits to ensure:
|
||||
- External processing activates correctly
|
||||
- Memory pressure is handled gracefully
|
||||
- Checkpointing works under load
|
||||
|
||||
### 2. **Failure Testing**
|
||||
|
||||
Test recovery scenarios:
|
||||
- Process crashes during batch processing
|
||||
- Memory pressure during operations
|
||||
- Network failures in distributed scenarios
|
||||
|
||||
### 3. **Performance Testing**
|
||||
|
||||
Measure:
|
||||
- Response times under various memory conditions
|
||||
- Throughput with different batch sizes
|
||||
- Resource utilization patterns
|
||||
|
||||
## Deployment Checklist
|
||||
|
||||
- [ ] Configure memory limits based on deployment environment
|
||||
- [ ] Set up monitoring and alerting
|
||||
- [ ] Configure persistent storage for checkpoints and cold cache
|
||||
- [ ] Test failover and recovery procedures
|
||||
- [ ] Document memory requirements and scaling limits
|
||||
- [ ] Configure appropriate logging levels
|
||||
- [ ] Set up distributed coordination (if using multiple nodes)
|
||||
- [ ] Verify health check endpoints
|
||||
- [ ] Test under expected production load
|
||||
|
||||
## Advanced Scenarios
|
||||
|
||||
### Multi-Node Deployment
|
||||
|
||||
For distributed deployments:
|
||||
1. Configure Redis for coordination
|
||||
2. Set unique node IDs
|
||||
3. Implement partition-aware processing
|
||||
4. Monitor cross-node communication
|
||||
|
||||
### High-Availability Setup
|
||||
|
||||
1. Use persistent checkpoint storage
|
||||
2. Implement automatic failover
|
||||
3. Configure redundant cache storage
|
||||
4. Monitor node health
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
1. Profile memory usage patterns
|
||||
2. Adjust algorithm selection thresholds
|
||||
3. Optimize batch sizes for your workload
|
||||
4. Configure appropriate parallelism levels
|
||||
|
||||
## Summary
|
||||
|
||||
This best practices project demonstrates how to build robust, memory-efficient applications using SqrtSpace SpaceTime. By following these patterns, you can build applications that:
|
||||
|
||||
- Scale gracefully under memory pressure
|
||||
- Process large datasets efficiently
|
||||
- Recover from failures automatically
|
||||
- Provide predictable performance
|
||||
- Optimize resource utilization
|
||||
|
||||
The key is to embrace the √n space-time tradeoff philosophy throughout your application architecture, letting the library handle the complexity of memory management while you focus on business logic.
|
||||
Reference in New Issue
Block a user