diff --git a/src/api/Elastic.Documentation.Api.Infrastructure/Caching/DynamoDbDistributedCache.cs b/src/api/Elastic.Documentation.Api.Infrastructure/Caching/DynamoDbDistributedCache.cs index 3b6f70791..e68cdf679 100644 --- a/src/api/Elastic.Documentation.Api.Infrastructure/Caching/DynamoDbDistributedCache.cs +++ b/src/api/Elastic.Documentation.Api.Infrastructure/Caching/DynamoDbDistributedCache.cs @@ -20,10 +20,6 @@ public sealed class DynamoDbDistributedCache(IAmazonDynamoDB dynamoDb, string ta { private static readonly ActivitySource ActivitySource = new(TelemetryConstants.CacheSourceName); - private readonly IAmazonDynamoDB _dynamoDb = dynamoDb; - private readonly string _tableName = tableName; - private readonly ILogger _logger = logger; - // DynamoDB attribute names private const string AttributeCacheKey = "CacheKey"; private const string AttributeValue = "Value"; @@ -34,24 +30,24 @@ public sealed class DynamoDbDistributedCache(IAmazonDynamoDB dynamoDb, string ta var hashedKey = key.Value; using var activity = ActivitySource.StartActivity("get cache", ActivityKind.Client); _ = (activity?.SetTag("cache.key", hashedKey)); - _ = (activity?.SetTag("cache.table", _tableName)); + _ = (activity?.SetTag("cache.table", tableName)); _ = (activity?.SetTag("cache.backend", "dynamodb")); try { - var response = await _dynamoDb.GetItemAsync(new GetItemRequest + var response = await dynamoDb.GetItemAsync(new GetItemRequest { - TableName = _tableName, + TableName = tableName, Key = new Dictionary { - [AttributeCacheKey] = new AttributeValue { S = hashedKey } + [AttributeCacheKey] = new() { S = hashedKey } } }, ct); if (!response.IsItemSet) { _ = (activity?.SetTag("cache.hit", false)); - _logger.LogDebug("Cache miss for key: {CacheKey}", hashedKey); + logger.LogDebug("Cache miss for key: {CacheKey}", hashedKey); return null; } @@ -64,7 +60,7 @@ public sealed class DynamoDbDistributedCache(IAmazonDynamoDB dynamoDb, string ta _ = (activity?.SetTag("cache.hit", value != null)); if (value != null) { - _logger.LogDebug("Cache hit for key: {CacheKey}", hashedKey); + logger.LogDebug("Cache hit for key: {CacheKey}", hashedKey); } return value; @@ -74,25 +70,25 @@ public sealed class DynamoDbDistributedCache(IAmazonDynamoDB dynamoDb, string ta // Table doesn't exist - return null gracefully // Infrastructure should create table, but don't fail hard in dev _ = (activity?.SetTag("cache.error", "table_not_found")); - _logger.LogWarning(ex, "DynamoDB table {TableName} not found. Cache operations will fail gracefully.", _tableName); + logger.LogWarning(ex, "DynamoDB table {TableName} not found. Cache operations will fail gracefully.", tableName); return null; } catch (ProvisionedThroughputExceededException ex) { _ = (activity?.SetTag("cache.error", "provisioned_throughput_exceeded")); - _logger.LogWarning(ex, "Provisioned throughput exceeded for DynamoDB cache table {TableName}.", _tableName); + logger.LogWarning(ex, "Provisioned throughput exceeded for DynamoDB cache table {TableName}.", tableName); return null; } catch (InternalServerErrorException ex) { _ = (activity?.SetTag("cache.error", "internal_server_error")); - _logger.LogError(ex, "Internal server error retrieving cache key {CacheKey} from DynamoDB", hashedKey); + logger.LogError(ex, "Internal server error retrieving cache key {CacheKey} from DynamoDB", hashedKey); return null; } catch (Exception ex) when (ex is not OperationCanceledException and not TaskCanceledException) { _ = (activity?.SetStatus(ActivityStatusCode.Error, ex.Message)); - _logger.LogError(ex, "Error retrieving cache key {CacheKey} from DynamoDB", hashedKey); + logger.LogError(ex, "Error retrieving cache key {CacheKey} from DynamoDB", hashedKey); return null; // Fail gracefully } // Allow cancellation exceptions to propagate to respect request lifetimes @@ -103,7 +99,7 @@ public async Task SetAsync(CacheKey key, string value, TimeSpan ttl, Cancel ct = var hashedKey = key.Value; using var activity = ActivitySource.StartActivity("set cache", ActivityKind.Client); _ = (activity?.SetTag("cache.key", hashedKey)); - _ = (activity?.SetTag("cache.table", _tableName)); + _ = (activity?.SetTag("cache.table", tableName)); _ = (activity?.SetTag("cache.backend", "dynamodb")); _ = (activity?.SetTag("cache.ttl", ttl.TotalSeconds)); @@ -112,41 +108,41 @@ public async Task SetAsync(CacheKey key, string value, TimeSpan ttl, Cancel ct = var expiresAt = DateTimeOffset.UtcNow.Add(ttl); var ttlTimestamp = expiresAt.ToUnixTimeSeconds(); - _ = await _dynamoDb.PutItemAsync(new PutItemRequest + _ = await dynamoDb.PutItemAsync(new PutItemRequest { - TableName = _tableName, + TableName = tableName, Item = new Dictionary { - [AttributeCacheKey] = new AttributeValue { S = hashedKey }, - [AttributeValue] = new AttributeValue { S = value }, - [AttributeTtl] = new AttributeValue { N = ttlTimestamp.ToString(CultureInfo.InvariantCulture) } + [AttributeCacheKey] = new() { S = hashedKey }, + [AttributeValue] = new() { S = value }, + [AttributeTtl] = new() { N = ttlTimestamp.ToString(CultureInfo.InvariantCulture) } } }, ct); - _logger.LogDebug("Cache set for key: {CacheKey}, TTL: {TTL}s", hashedKey, ttl.TotalSeconds); + logger.LogDebug("Cache set for key: {CacheKey}, TTL: {TTL}s", hashedKey, ttl.TotalSeconds); } catch (ResourceNotFoundException ex) { // Table doesn't exist - fail silently in dev, log in production // Infrastructure should create table before deployment - _ = (activity?.SetTag("cache.error", "table_not_found")); - _logger.LogWarning(ex, "DynamoDB table {TableName} not found. Unable to cache key {CacheKey}.", _tableName, hashedKey); + _ = activity?.SetTag("cache.error", "table_not_found"); + logger.LogWarning(ex, "DynamoDB table {TableName} not found. Unable to cache key {CacheKey}.", tableName, hashedKey); } catch (ProvisionedThroughputExceededException ex) { - _ = (activity?.SetTag("cache.error", "provisioned_throughput_exceeded")); - _logger.LogWarning(ex, "Provisioned throughput exceeded for DynamoDB cache table {TableName}. Unable to cache key {CacheKey}.", _tableName, hashedKey); + _ = activity?.SetTag("cache.error", "provisioned_throughput_exceeded"); + logger.LogWarning(ex, "Provisioned throughput exceeded for DynamoDB cache table {TableName}. Unable to cache key {CacheKey}.", tableName, hashedKey); } catch (InternalServerErrorException ex) { - _ = (activity?.SetTag("cache.error", "internal_server_error")); - _logger.LogError(ex, "Internal server error setting cache key {CacheKey} in DynamoDB", hashedKey); + _ = activity?.SetTag("cache.error", "internal_server_error"); + logger.LogError(ex, "Internal server error setting cache key {CacheKey} in DynamoDB", hashedKey); } catch (Exception ex) when (ex is not OperationCanceledException and not TaskCanceledException) { // Allow cancellation exceptions to propagate to respect request lifetimes _ = activity?.SetStatus(ActivityStatusCode.Error, ex.Message); - _logger.LogError(ex, "Error setting cache key {CacheKey} in DynamoDB", hashedKey); + logger.LogError(ex, "Error setting cache key {CacheKey} in DynamoDB", hashedKey); // Fail gracefully - don't throw } } diff --git a/src/api/Elastic.Documentation.Api.Infrastructure/ServicesExtension.cs b/src/api/Elastic.Documentation.Api.Infrastructure/ServicesExtension.cs index 88ebaba3d..7bcbfa3ad 100644 --- a/src/api/Elastic.Documentation.Api.Infrastructure/ServicesExtension.cs +++ b/src/api/Elastic.Documentation.Api.Infrastructure/ServicesExtension.cs @@ -152,7 +152,7 @@ private static void AddDistributedCache(IServiceCollection services, AppEnv appE _ = services.AddSingleton(sp => { var dynamoDb = sp.GetRequiredService(); - var tableName = $"docs-api-cache-{appEnv.ToStringFast()}"; + var tableName = $"docs-api-cache-{appEnv.ToStringFast(true)}"; var dynamoLogger = sp.GetRequiredService>(); var multiLogger = sp.GetRequiredService>();