Initial push
This commit is contained in:
166
samples/SampleWebApi/Controllers/ProductsController.cs
Normal file
166
samples/SampleWebApi/Controllers/ProductsController.cs
Normal 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; }
|
||||
}
|
||||
Reference in New Issue
Block a user