Initial push

This commit is contained in:
2025-07-20 03:41:39 -04:00
commit d315f5d26e
118 changed files with 25819 additions and 0 deletions

View File

@@ -0,0 +1,158 @@
using Microsoft.AspNetCore.Mvc;
using SqrtSpace.SpaceTime.AspNetCore;
using SqrtSpace.SpaceTime.Core;
using SampleWebApi.Models;
using SampleWebApi.Services;
namespace SampleWebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class AnalyticsController : ControllerBase
{
private readonly IOrderAnalyticsService _analyticsService;
private readonly ILogger<AnalyticsController> _logger;
public AnalyticsController(IOrderAnalyticsService analyticsService, ILogger<AnalyticsController> logger)
{
_analyticsService = analyticsService;
_logger = logger;
}
/// <summary>
/// Calculate revenue by category using memory-efficient aggregation
/// </summary>
/// <remarks>
/// This endpoint demonstrates using external grouping for large datasets.
/// When processing millions of orders, it automatically uses disk-based
/// aggregation to stay within memory limits.
/// </remarks>
[HttpGet("revenue-by-category")]
public async Task<ActionResult<IEnumerable<CategoryRevenue>>> GetRevenueByCategory(
[FromQuery] DateTime? startDate = null,
[FromQuery] DateTime? endDate = null)
{
var result = await _analyticsService.GetRevenueByCategoryAsync(startDate, endDate);
return Ok(result);
}
/// <summary>
/// Get top customers using external sorting
/// </summary>
/// <remarks>
/// This endpoint finds top customers by order value using external sorting.
/// Even with millions of customers, it maintains O(√n) memory usage.
/// </remarks>
[HttpGet("top-customers")]
public async Task<ActionResult<IEnumerable<CustomerSummary>>> GetTopCustomers(
[FromQuery] int top = 100,
[FromQuery] DateTime? since = null)
{
if (top > 1000)
{
return BadRequest("Cannot retrieve more than 1000 customers at once");
}
var customers = await _analyticsService.GetTopCustomersAsync(top, since);
return Ok(customers);
}
/// <summary>
/// Stream real-time order analytics
/// </summary>
/// <remarks>
/// This endpoint streams analytics data in real-time using Server-Sent Events (SSE).
/// It demonstrates memory-efficient streaming of continuous data.
/// </remarks>
[HttpGet("real-time/orders")]
[SpaceTimeStreaming]
public async Task StreamOrderAnalytics(CancellationToken cancellationToken)
{
Response.ContentType = "text/event-stream";
Response.Headers.Append("Cache-Control", "no-cache");
Response.Headers.Append("X-Accel-Buffering", "no");
await foreach (var analytics in _analyticsService.StreamRealTimeAnalyticsAsync(cancellationToken))
{
var data = System.Text.Json.JsonSerializer.Serialize(analytics);
await Response.WriteAsync($"data: {data}\n\n", cancellationToken);
await Response.Body.FlushAsync(cancellationToken);
// Small delay to simulate real-time updates
await Task.Delay(1000, cancellationToken);
}
}
/// <summary>
/// Generate complex report with checkpointing
/// </summary>
/// <remarks>
/// This endpoint generates a complex report that may take a long time.
/// It uses checkpointing to allow resuming if the operation is interrupted.
/// The report includes multiple aggregations and can handle billions of records.
/// </remarks>
[HttpPost("reports/generate")]
[EnableCheckpoint(Strategy = CheckpointStrategy.SqrtN)]
public async Task<ActionResult<ReportResult>> GenerateReport(
[FromBody] ReportRequest request,
[FromHeader(Name = "X-Report-Id")] string? reportId = null)
{
reportId ??= Guid.NewGuid().ToString();
var checkpoint = HttpContext.Features.Get<ICheckpointFeature>();
ReportState? previousState = null;
if (checkpoint != null)
{
previousState = await checkpoint.CheckpointManager.RestoreLatestCheckpointAsync<ReportState>();
if (previousState != null)
{
_logger.LogInformation("Resuming report generation from checkpoint. Progress: {progress}%",
previousState.ProgressPercent);
}
}
var result = await _analyticsService.GenerateComplexReportAsync(
request,
reportId,
previousState,
checkpoint?.CheckpointManager);
return Ok(result);
}
/// <summary>
/// Analyze order patterns using machine learning with batched processing
/// </summary>
/// <remarks>
/// This endpoint demonstrates processing large datasets for ML analysis
/// using √n batching to maintain memory efficiency while computing features.
/// </remarks>
[HttpPost("analyze-patterns")]
public async Task<ActionResult<PatternAnalysisResult>> AnalyzeOrderPatterns(
[FromBody] PatternAnalysisRequest request)
{
if (request.MaxOrdersToAnalyze > 1_000_000)
{
return BadRequest("Cannot analyze more than 1 million orders in a single request");
}
var result = await _analyticsService.AnalyzeOrderPatternsAsync(request);
return Ok(result);
}
/// <summary>
/// Get memory usage statistics for the analytics operations
/// </summary>
/// <remarks>
/// This endpoint provides insights into how SpaceTime is managing memory
/// for analytics operations, useful for monitoring and optimization.
/// </remarks>
[HttpGet("memory-stats")]
public ActionResult<MemoryStatistics> GetMemoryStatistics()
{
var stats = _analyticsService.GetMemoryStatistics();
return Ok(stats);
}
}

View File

@@ -0,0 +1,166 @@
using Microsoft.AspNetCore.Mvc;
using SqrtSpace.SpaceTime.AspNetCore;
using SqrtSpace.SpaceTime.Core;
using SampleWebApi.Models;
using SampleWebApi.Services;
namespace SampleWebApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
private readonly ILogger<ProductsController> _logger;
public ProductsController(IProductService productService, ILogger<ProductsController> logger)
{
_productService = productService;
_logger = logger;
}
/// <summary>
/// Get all products with memory-efficient paging
/// </summary>
/// <remarks>
/// This endpoint demonstrates basic pagination to limit memory usage.
/// For very large datasets, consider using the streaming endpoint instead.
/// </remarks>
[HttpGet]
public async Task<ActionResult<PagedResult<Product>>> GetProducts(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 100)
{
if (pageSize > 1000)
{
return BadRequest("Page size cannot exceed 1000 items");
}
var result = await _productService.GetProductsPagedAsync(page, pageSize);
return Ok(result);
}
/// <summary>
/// Stream products using √n batching for memory efficiency
/// </summary>
/// <remarks>
/// This endpoint streams large datasets using √n-sized batches.
/// It's ideal for processing millions of records without loading them all into memory.
/// The response is streamed as newline-delimited JSON (NDJSON).
/// </remarks>
[HttpGet("stream")]
[SpaceTimeStreaming(ChunkStrategy = ChunkStrategy.SqrtN)]
public async IAsyncEnumerable<Product> StreamProducts(
[FromQuery] string? category = null,
[FromQuery] decimal? minPrice = null)
{
await foreach (var product in _productService.StreamProductsAsync(category, minPrice))
{
yield return product;
}
}
/// <summary>
/// Search products with memory-aware filtering
/// </summary>
/// <remarks>
/// This endpoint uses external sorting when the result set is large,
/// automatically spilling to disk if memory pressure is detected.
/// </remarks>
[HttpGet("search")]
public async Task<ActionResult<IEnumerable<Product>>> SearchProducts(
[FromQuery] string query,
[FromQuery] string? sortBy = "name",
[FromQuery] bool descending = false)
{
if (string.IsNullOrWhiteSpace(query))
{
return BadRequest("Search query is required");
}
var results = await _productService.SearchProductsAsync(query, sortBy, descending);
return Ok(results);
}
/// <summary>
/// Bulk update product prices with checkpointing
/// </summary>
/// <remarks>
/// This endpoint demonstrates checkpoint-enabled bulk operations.
/// If the operation fails, it can be resumed from the last checkpoint.
/// Pass the same operationId to resume a failed operation.
/// </remarks>
[HttpPost("bulk-update-prices")]
[EnableCheckpoint(Strategy = CheckpointStrategy.Linear)]
public async Task<ActionResult<BulkUpdateResult>> BulkUpdatePrices(
[FromBody] BulkPriceUpdateRequest request,
[FromHeader(Name = "X-Operation-Id")] string? operationId = null)
{
operationId ??= Guid.NewGuid().ToString();
var checkpoint = HttpContext.Features.Get<ICheckpointFeature>();
if (checkpoint != null)
{
// Try to restore from previous checkpoint
var state = await checkpoint.CheckpointManager.RestoreLatestCheckpointAsync<BulkUpdateState>();
if (state != null)
{
_logger.LogInformation("Resuming bulk update from checkpoint. Processed: {count}", state.ProcessedCount);
}
}
var result = await _productService.BulkUpdatePricesAsync(
request.CategoryFilter,
request.PriceMultiplier,
operationId,
checkpoint?.CheckpointManager);
return Ok(result);
}
/// <summary>
/// Export products to CSV with memory streaming
/// </summary>
/// <remarks>
/// This endpoint exports products to CSV format using streaming to minimize memory usage.
/// Even millions of products can be exported without loading them all into memory.
/// </remarks>
[HttpGet("export/csv")]
public async Task ExportToCsv([FromQuery] string? category = null)
{
Response.ContentType = "text/csv";
Response.Headers.Append("Content-Disposition", $"attachment; filename=products_{DateTime.UtcNow:yyyyMMdd}.csv");
await _productService.ExportToCsvAsync(Response.Body, category);
}
/// <summary>
/// Get product price statistics using memory-efficient aggregation
/// </summary>
/// <remarks>
/// This endpoint calculates statistics over large datasets using external aggregation
/// when memory pressure is detected.
/// </remarks>
[HttpGet("statistics")]
public async Task<ActionResult<ProductStatistics>> GetStatistics([FromQuery] string? category = null)
{
var stats = await _productService.GetStatisticsAsync(category);
return Ok(stats);
}
}
public class BulkPriceUpdateRequest
{
public string? CategoryFilter { get; set; }
public decimal PriceMultiplier { get; set; }
}
public class BulkUpdateResult
{
public string OperationId { get; set; } = "";
public int TotalProducts { get; set; }
public int UpdatedProducts { get; set; }
public int FailedProducts { get; set; }
public bool Completed { get; set; }
public string? CheckpointId { get; set; }
}