diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 3729ff0c..00000000
--- a/.dockerignore
+++ /dev/null
@@ -1,25 +0,0 @@
-**/.classpath
-**/.dockerignore
-**/.env
-**/.git
-**/.gitignore
-**/.project
-**/.settings
-**/.toolstarget
-**/.vs
-**/.vscode
-**/*.*proj.user
-**/*.dbmdl
-**/*.jfm
-**/azds.yaml
-**/bin
-**/charts
-**/docker-compose*
-**/Dockerfile*
-**/node_modules
-**/npm-debug.log
-**/obj
-**/secrets.dev.yaml
-**/values.dev.yaml
-LICENSE
-README.md
\ No newline at end of file
diff --git a/.env b/.env
index e3a59bff..4c96b33e 100644
--- a/.env
+++ b/.env
@@ -7,19 +7,19 @@ ELASTIC_VERSION=8.7.1
#
# Superuser role, full access to cluster management and data indices.
# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html
-ELASTIC_PASSWORD='admin123@'
+ELASTIC_PASSWORD='changeme'
# User 'logstash_internal' (custom)
#
# The user Logstash uses to connect and send data to Elasticsearch.
# https://www.elastic.co/guide/en/logstash/current/ls-security.html
-LOGSTASH_INTERNAL_PASSWORD='admin123@'
+LOGSTASH_INTERNAL_PASSWORD='changeme'
# User 'kibana_system' (built-in)
#
# The user Kibana uses to connect and communicate with Elasticsearch.
# https://www.elastic.co/guide/en/elasticsearch/reference/current/built-in-users.html
-KIBANA_SYSTEM_PASSWORD='admin123@'
+KIBANA_SYSTEM_PASSWORD='changeme'
# Users 'metricbeat_internal', 'filebeat_internal' and 'heartbeat_internal' (custom)
#
diff --git a/docker-compose.dcproj b/docker-compose.dcproj
index 9e42c14b..22eaa2fd 100644
--- a/docker-compose.dcproj
+++ b/docker-compose.dcproj
@@ -14,7 +14,6 @@
docker-compose.yml
-
diff --git a/docker-compose.yml b/docker-compose.yml
index 596a193e..eef025df 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,32 +8,32 @@ services:
dockerfile: host/YANLib.HttpApi.Host/Dockerfile
setup:
+ profiles:
+ - setup
build:
context: setup/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
init: true
- container_name: setup
volumes:
- ./setup/entrypoint.sh:/entrypoint.sh:ro,Z
- ./setup/lib.sh:/lib.sh:ro,Z
- ./setup/roles:/roles:ro,Z
- - setup:/state:Z
environment:
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
+ KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
+ BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-}
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
METRICBEAT_INTERNAL_PASSWORD: ${METRICBEAT_INTERNAL_PASSWORD:-}
FILEBEAT_INTERNAL_PASSWORD: ${FILEBEAT_INTERNAL_PASSWORD:-}
HEARTBEAT_INTERNAL_PASSWORD: ${HEARTBEAT_INTERNAL_PASSWORD:-}
MONITORING_INTERNAL_PASSWORD: ${MONITORING_INTERNAL_PASSWORD:-}
- KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
- BEATS_SYSTEM_PASSWORD: ${BEATS_SYSTEM_PASSWORD:-}
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-}
KAFKA_CLIENT_USERS: ${KAFKA_CLIENT_USERS:-}
KAFKA_CLIENT_PASSWORDS: ${KAFKA_CLIENT_PASSWORDS:-}
networks:
- - demo
+ - yanlib
depends_on:
- elasticsearch
@@ -42,7 +42,6 @@ services:
context: elasticsearch/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
- container_name: elasticsearch
volumes:
- ./elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml:ro,Z
- elasticsearch:/usr/share/elasticsearch/data:Z
@@ -50,15 +49,17 @@ services:
- 9200:9200
- 9300:9300
environment:
+ node.name: elasticsearch
ES_JAVA_OPTS: -Xms512m -Xmx512m
# Bootstrap password.
# Used to initialize the keystore during the initial startup of
# Elasticsearch. Ignored on subsequent runs.
ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}
# Use single node discovery in order to disable production mode and avoid bootstrap checks.
- # see: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html
+ # see: https://www.elastic.co/guide/en/elasticsearch/reference/7.17/bootstrap-checks.html
+ discovery.type: single-node
networks:
- - demo
+ - yanlib
restart: unless-stopped
logstash:
@@ -66,7 +67,6 @@ services:
context: logstash/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
- container_name: logstash
volumes:
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml:ro,Z
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro,Z
@@ -80,7 +80,7 @@ services:
LS_JAVA_OPTS: -Xms256m -Xmx256m
LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}
networks:
- - demo
+ - yanlib
depends_on:
- elasticsearch
restart: unless-stopped
@@ -90,7 +90,6 @@ services:
context: kibana/
args:
ELASTIC_VERSION: ${ELASTIC_VERSION}
- container_name: kibana
volumes:
- ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml:ro,Z
ports:
@@ -98,14 +97,13 @@ services:
environment:
KIBANA_SYSTEM_PASSWORD: ${KIBANA_SYSTEM_PASSWORD:-}
networks:
- - demo
+ - yanlib
depends_on:
- elasticsearch
restart: unless-stopped
rabbitmq:
image: rabbitmq:3-management
- container_name: rabbitmq
ports:
- 5672:5672
- 15672:15672
@@ -113,27 +111,25 @@ services:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-}
networks:
- - demo
+ - yanlib
depends_on:
- logstash
restart: unless-stopped
zookeeper:
image: bitnami/zookeeper:latest
- container_name: zookeeper
ports:
- 2181:2181
environment:
- ALLOW_ANONYMOUS_LOGIN=yes
networks:
- - demo
+ - yanlib
depends_on:
- logstash
restart: unless-stopped
kafka:
image: bitnami/kafka:latest
- container_name: kafka
ports:
- 9092:9092
- 9093:9093
@@ -150,14 +146,13 @@ services:
KAFKA_CLIENT_USERS: ${KAFKA_CLIENT_USERS:-}
KAFKA_CLIENT_PASSWORDS: ${KAFKA_CLIENT_PASSWORDS:-}
networks:
- - demo
+ - yanlib
depends_on:
- zookeeper
restart: unless-stopped
kafka-ui:
image: provectuslabs/kafka-ui:latest
- container_name: kafka-ui
ports:
- 8080:8080
environment:
@@ -165,7 +160,7 @@ services:
- KAFKA_CLUSTERS_0_ZOOKEEPER=zookeeper:2181
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka:9093
networks:
- - demo
+ - yanlib
depends_on:
- zookeeper
- kafka
@@ -173,18 +168,17 @@ services:
redis:
image: redis:latest
- container_name: redis
command: redis-server --requirepass ${REDIS_PASS:-}
ports:
- 6379:6379
networks:
- - demo
+ - yanlib
restart: unless-stopped
networks:
- demo:
+ yanlib:
driver: bridge
volumes:
setup:
- elasticsearch:
+ elasticsearch:
\ No newline at end of file
diff --git a/elasticsearch/config/elasticsearch.yml b/elasticsearch/config/elasticsearch.yml
index 427f4b4b..3cc34bbc 100644
--- a/elasticsearch/config/elasticsearch.yml
+++ b/elasticsearch/config/elasticsearch.yml
@@ -5,15 +5,11 @@
cluster.name: docker-cluster
network.host: 0.0.0.0
-node.name: elasticsearch
-
-discovery.type: single-node
-
## X-Pack settings
## see https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html
#
xpack.license.self_generated.type: trial
-xpack.security.enabled: true
+xpack.security.enabled: false
## Set the built-in users' passwords.
# Run the following command from the Elasticsearch directory:
diff --git a/host/YANLib.HttpApi.Host/Program.cs b/host/YANLib.HttpApi.Host/Program.cs
index 0cf98504..ffecdb71 100644
--- a/host/YANLib.HttpApi.Host/Program.cs
+++ b/host/YANLib.HttpApi.Host/Program.cs
@@ -5,8 +5,6 @@
using System;
using System.Threading.Tasks;
using static Microsoft.AspNetCore.Builder.WebApplication;
-using static Serilog.Events.LogEventLevel;
-using static System.DateTime;
namespace YANLib;
@@ -14,20 +12,13 @@ public class Program
{
public async static Task Main(string[] args)
{
- Log.Logger = new LoggerConfiguration()
-#if DEBUG
- .MinimumLevel.Debug()
-#else
- .MinimumLevel.Information()
-#endif
- .MinimumLevel.Override("Microsoft", Information).MinimumLevel.Override("Microsoft.EntityFrameworkCore", Warning).Enrich.FromLogContext().WriteTo.Async(c => c.File($"Logs/{Now:yyyy-MM-dd}.log")).WriteTo.Async(c => c.Console()).CreateLogger();
-
+ Log.Logger = new LoggerConfiguration().MinimumLevel.Information().Enrich.FromLogContext().WriteTo.Async(c => c.Console()).CreateLogger();
try
{
Log.Information("Starting YANLib.HttpApi.Host.");
var builder = CreateBuilder(args);
- builder.Host.AddAppSettingsSecretsJson().UseAutofac().UseSerilog();
- await builder.AddApplicationAsync();
+ _ = builder.Host.AddAppSettingsSecretsJson().UseAutofac().UseSerilog((t, f) => f.Enrich.FromLogContext().ReadFrom.Configuration(t.Configuration));
+ _ = await builder.AddApplicationAsync();
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
diff --git a/host/YANLib.HttpApi.Host/YANLib.HttpApi.Host.csproj b/host/YANLib.HttpApi.Host/YANLib.HttpApi.Host.csproj
index 7f139473..da75e30d 100644
--- a/host/YANLib.HttpApi.Host/YANLib.HttpApi.Host.csproj
+++ b/host/YANLib.HttpApi.Host/YANLib.HttpApi.Host.csproj
@@ -13,15 +13,24 @@
-
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
-
+
diff --git a/host/YANLib.HttpApi.Host/YANLibHttpApiHostModule.cs b/host/YANLib.HttpApi.Host/YANLibHttpApiHostModule.cs
index 9281a8dd..d53e399b 100644
--- a/host/YANLib.HttpApi.Host/YANLibHttpApiHostModule.cs
+++ b/host/YANLib.HttpApi.Host/YANLibHttpApiHostModule.cs
@@ -14,7 +14,10 @@
using Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonXLite;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared;
using Volo.Abp.AspNetCore.Serilog;
+using Volo.Abp.Auditing;
using Volo.Abp.Autofac;
+using Volo.Abp.Caching.StackExchangeRedis;
+using Volo.Abp.Http.Client;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
@@ -25,14 +28,16 @@
namespace YANLib;
[DependsOn(
-typeof(YANLibHttpApiModule),
- typeof(AbpAutofacModule),
- typeof(AbpAspNetCoreMultiTenancyModule),
+ typeof(YANLibHttpApiModule),
typeof(YANLibApplicationModule),
typeof(YANLibEntityFrameworkCoreModule),
+ typeof(AbpAutofacModule),
+ typeof(AbpAspNetCoreMultiTenancyModule),
typeof(AbpAspNetCoreMvcUiLeptonXLiteThemeModule),
typeof(AbpAspNetCoreSerilogModule),
- typeof(AbpSwashbuckleModule)
+ typeof(AbpSwashbuckleModule),
+ typeof(AbpCachingStackExchangeRedisModule),
+ typeof(AbpHttpClientModule)
)]
public class YANLibHttpApiHostModule : AbpModule
{
diff --git a/host/YANLib.HttpApi.Host/appsettings.Development.json b/host/YANLib.HttpApi.Host/appsettings.Development.json
index 9cd9daa0..dd26cc08 100644
--- a/host/YANLib.HttpApi.Host/appsettings.Development.json
+++ b/host/YANLib.HttpApi.Host/appsettings.Development.json
@@ -33,5 +33,70 @@
"TestApi": {
"BaseUrl": "http://test-api.local/"
}
+ },
+ "Serilog": {
+ // Serilog.Settings.Configuration
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft": "Information"
+ }
+ },
+ "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ],
+ "WriteTo": [
+ {
+ // Serilog.Sinks.Async
+ "Name": "Async",
+ "Args": {
+ "configure": [
+ {
+ // Serilog.Sinks.Console
+ "Name": "Console"
+ },
+ {
+ // Serilog.Sinks.File
+ "Name": "File",
+ "Args": {
+ "path": "Logs/.log",
+ "rollingInterval": "Hour",
+ "encoding": "System.Text.Encoding::UTF8"
+ }
+ },
+ {
+ // Serilog.Formatting.Compact
+ "Name": "File",
+ "Args": {
+ "path": "Logs/.json",
+ "rollingInterval": "Hour",
+ "formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
+ }
+ }
+ ]
+ }
+ },
+ {
+ // Serilog.Sinks.Elasticsearch
+ "Name": "Elasticsearch",
+ "Args": {
+ "nodeUris": "http://localhost:9200",
+ "indexFormat": "log-dev-yanlib-{0:yyyy.MM.dd}",
+ "autoRegisterTemplate": true,
+ "autoRegisterTemplateVersion": "ESv8"
+ }
+ }
+ ]
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Information"
+ },
+ "ElasticApm": {
+ "Enabled": true,
+ "ServerUrls": "http://localhost:8200",
+ "ServiceName": "YANLib",
+ "Environment": "Development",
+ "SecretToken": ""
+ }
}
}
\ No newline at end of file
diff --git a/setup/.dockerignore b/setup/.dockerignore
index 02f22440..c5dd1c85 100644
--- a/setup/.dockerignore
+++ b/setup/.dockerignore
@@ -7,6 +7,3 @@ Dockerfile
# Ignore Git files
.gitignore
-
-# Ignore setup state
-state/
diff --git a/src/YANLib.Application.Redis/Services/IRedisService.cs b/src/YANLib.Application.Redis/Services/IRedisService.cs
index 6e7eaafb..7a2b09f1 100644
--- a/src/YANLib.Application.Redis/Services/IRedisService.cs
+++ b/src/YANLib.Application.Redis/Services/IRedisService.cs
@@ -4,12 +4,14 @@ namespace YANLib.Application.Redis.Services;
public interface IRedisService : IApplicationService
{
- public ValueTask?> GetAll(string group);
public ValueTask Get(string group, string key);
- public ValueTask?> GetBulk(string group, params string[] keys);
- public Task Set(string group, string key, T value);
- public Task SetBulk(string group, IDictionary fields);
- public ValueTask DeleteAll(string group);
+ public ValueTask?> GetBulk(string group, params string[] keys);
+ public ValueTask?> GetAll(string group);
+ public ValueTask Set(string group, string key, T value);
+ public ValueTask SetBulk(string group, IDictionary fields);
public ValueTask Delete(string group, string key);
public ValueTask DeleteBulk(string group, params string[] keys);
+ public ValueTask DeleteAll(string group);
+ public ValueTask?>?> GetGroup(string groupPreffix);
+ public ValueTask DeleteGroup(string groupPreffix);
}
diff --git a/src/YANLib.Application.Redis/Services/Implements/RedisService.cs b/src/YANLib.Application.Redis/Services/Implements/RedisService.cs
index 0ae4c6e7..012cf3ce 100644
--- a/src/YANLib.Application.Redis/Services/Implements/RedisService.cs
+++ b/src/YANLib.Application.Redis/Services/Implements/RedisService.cs
@@ -1,104 +1,295 @@
-using StackExchange.Redis;
+using Microsoft.Extensions.Logging;
+using StackExchange.Redis;
+using Volo.Abp;
using YANLib.Application.Redis.ConnectionFactory;
using YANLib.Dtos;
+using YANLib.Exceptions;
using static System.Text.Encoding;
+using static System.Threading.Tasks.Task;
+using static YANLib.Exceptions.ExceptionMessage;
namespace YANLib.Application.Redis.Services.Implements;
-public class RedisService : IRedisService
+public class RedisService : IRedisService
{
#region Fields
- private readonly IRedisConnectionFactory _redisConnectionFactory;
+ private readonly ILogger _logger;
+ private readonly IRedisConnectionFactory _connectionFactory;
+ private readonly ConnectionMultiplexer _connectionMultiplexer;
private readonly IDatabase _database;
#endregion
#region Constructors
- public RedisService(IRedisConnectionFactory redisConnectionFactory)
+ public RedisService(ILogger logger, IRedisConnectionFactory connectionFactory)
{
- _redisConnectionFactory = redisConnectionFactory;
- _database = _redisConnectionFactory.Connection().GetDatabase();
+ _logger = logger;
+ _connectionFactory = connectionFactory;
+ _connectionMultiplexer = _connectionFactory.Connection();
+ _database = _connectionMultiplexer.GetDatabase();
}
#endregion
#region Implements
- public async ValueTask?> GetAll(string group)
+ public async ValueTask Get(string group, string key)
{
- if (group.IsWhiteSpaceOrNull())
+ try
{
- return default;
+ if (group.IsWhiteSpaceOrNull() || key.IsWhiteSpaceOrNull())
+ {
+ throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST);
+ }
+ var val = await _database.HashGetAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant());
+ return val.HasValue ? UTF8.GetString(val!).Deserialize() : default;
}
- var rslts = new Dictionary();
- foreach (var item in (await _database.HashGetAllAsync(group.ToLowerInvariant())).Where(e => e.Name.HasValue && e.Value.HasValue))
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "GetRedisService-Exception: {Group} ; {Key}", group, key);
+ throw;
+ }
+ }
+
+ public async ValueTask?> GetBulk(string group, params string[] keys)
+ {
+ try
{
- var val = item.Value;
- if (val.HasValue)
+ if (group.IsWhiteSpaceOrNull() || keys.AllWhiteSpaceOrNull())
{
- rslts.Add(item.Name.ToString(), UTF8.GetString(val!).Deserialize());
+ throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST);
}
+ var rslts = new Dictionary();
+ var semSlim = new SemaphoreSlim(1);
+ await WhenAll(keys.Select(async k =>
+ {
+ var val = await _database.HashGetAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)k.ToLowerInvariant());
+ if (val.HasValue)
+ {
+ await semSlim.WaitAsync();
+ try
+ {
+ rslts.Add(k, UTF8.GetString(val!).Deserialize());
+ }
+ finally
+ {
+ _ = semSlim.Release();
+ }
+ }
+ }));
+ return rslts;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "GetBulkRedisService-Exception: {Group} ; {Keys}", group, string.Join(", ", keys));
+ throw;
}
- return rslts;
}
- public async ValueTask Get(string group, string key)
+ public async ValueTask?> GetAll(string group)
{
- if (group.IsWhiteSpaceOrNull() || key.IsWhiteSpaceOrNull())
+ try
{
- return default;
+ if (group.IsWhiteSpaceOrNull())
+ {
+ throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST);
+ }
+ var rslts = new Dictionary();
+ var semSlim = new SemaphoreSlim(1);
+ await WhenAll((await _database.HashGetAllAsync(group.ToLowerInvariant())).Where(e => e.Name.HasValue && e.Value.HasValue).Select(async x =>
+ {
+ var val = x.Value;
+ if (val.HasValue)
+ {
+ await semSlim.WaitAsync();
+ try
+ {
+ rslts.Add(x.Name.ToString(), UTF8.GetString(val!).Deserialize());
+ }
+ finally
+ {
+ _ = semSlim.Release();
+ }
+ }
+ }));
+ return rslts;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "GetAllRedisService-Exception: {Group}", group);
+ throw;
}
- var val = await _database.HashGetAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant());
- return val.HasValue ? UTF8.GetString(val!).Deserialize() : default;
}
- public async ValueTask?> GetBulk(string group, params string[] keys)
+ public async ValueTask Set(string group, string key, JsonDto value)
{
- if (group.IsWhiteSpaceOrNull() || keys.AllWhiteSpaceOrNull())
+ var jsonVal = value.CamelSerialize();
+ try
{
- return default;
+ return group.IsWhiteSpaceOrNull() || key.IsWhiteSpaceOrNull() || value is null
+ ? throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST)
+ : await Delete(group, key) && await _database.HashSetAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant(), jsonVal);
}
- var rslts = new Dictionary();
- foreach (var key in keys)
+ catch (Exception ex)
{
- var val = await _database.HashGetAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant());
- if (val.HasValue)
+ _logger.LogError(ex, "SetRedisService-Exception: {Group} ; {Key} ; {Value}", group, key, jsonVal);
+ throw;
+ }
+ }
+
+ public async ValueTask SetBulk(string group, IDictionary fields)
+ {
+ try
+ {
+ if (group.IsWhiteSpaceOrNull() || fields.IsEmptyOrNull())
{
- rslts.Add(key, UTF8.GetString(val!).Deserialize());
+ throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST);
}
+ await _database.HashSetAsync(group.ToLowerInvariant(), fields.Select(p => new HashEntry(p.Key.ToLowerInvariant(), p.Value.CamelSerialize())).ToArray());
+ return true;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "SetBulkRedisService-Exception: {Group} ; {Fields}", group, fields.CamelSerialize());
+ throw;
}
- return rslts;
}
- public Task Set(string group, string key, RedisDto value) => _database.HashSetAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant(), value.CamelSerialize());
+ public async ValueTask Delete(string group, string key)
+ {
+ try
+ {
+ return group.IsWhiteSpaceOrNull() || key.IsWhiteSpaceOrNull()
+ ? throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST)
+ : await _database.HashDeleteAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant());
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "DeleteRedisService-Exception: {Group} ; {Key}", group, key);
+ throw;
+ }
+ }
- public async Task SetBulk(string group, IDictionary fields)
+ public async ValueTask DeleteBulk(string group, params string[] keys)
{
- if (group.IsNotWhiteSpaceAndNull() && fields.IsNotEmptyAndNull())
+ try
{
- await _database.HashSetAsync(group.ToLowerInvariant(), fields.Select(p => new HashEntry(p.Key.ToLowerInvariant(), p.Value.CamelSerialize())).ToArray());
+ if (group.IsWhiteSpaceOrNull() || keys.AllWhiteSpaceOrNull())
+ {
+ throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST);
+ }
+ var rslt = true;
+ var semSlim = new SemaphoreSlim(1);
+ await WhenAll(keys.Where(k => k.IsNotWhiteSpaceAndNull()).Select(async k =>
+ {
+ await semSlim.WaitAsync();
+ try
+ {
+ rslt = rslt && await _database.HashDeleteAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)k.ToLowerInvariant());
+ }
+ finally
+ {
+ _ = semSlim.Release();
+ }
+ }));
+ return rslt;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "DeleteBulkRedisService-Exception: {Group} ; {Keys}", group, string.Join(", ", keys));
+ throw;
}
}
public async ValueTask DeleteAll(string group)
{
- var dic = await GetAll(group);
- return dic!.IsNotEmptyAndNull() && await DeleteBulk(group, dic!.Select(p => p.Key).ToArray());
+ try
+ {
+ if (group.IsWhiteSpaceOrNull())
+ {
+ throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST);
+ }
+ var dic = await GetAll(group);
+ return dic!.IsEmptyOrNull() || await DeleteBulk(group, dic!.Select(p => p.Key).ToArray());
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "DeleteAllRedisService-Exception: {Group}", group);
+ throw;
+ }
}
- public async ValueTask Delete(string group, string key) => group.IsNotWhiteSpaceAndNull()
- && key.IsNotWhiteSpaceAndNull()
- && await _database.HashDeleteAsync((RedisKey)group.ToLowerInvariant(), (RedisValue)key.ToLowerInvariant());
+ public async ValueTask?>?> GetGroup(string groupPreffix)
+ {
+ try
+ {
+ var redisRslt = await GetGroupKeys(groupPreffix);
+ if (redisRslt is not null)
+ {
+ var keys = (RedisKey[])redisRslt!;
+ var rslts = new Dictionary?>();
+ if (keys.IsNotEmptyAndNull())
+ {
+ var semSlim = new SemaphoreSlim(1);
+ await WhenAll(keys.Select(async k =>
+ {
+ var dic = await GetAll(k!);
+ if (dic!.IsNotEmptyAndNull())
+ {
+ await semSlim.WaitAsync();
+ try
+ {
+ rslts.Add(k.ToString().Split(":").Reverse().FirstOrDefault() ?? string.Empty, dic!);
+ }
+ finally
+ {
+ _ = semSlim.Release();
+ }
+ }
+ }));
+ }
+ return rslts;
+ }
+ return default;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "GetGroupRedisService-Exception: {GroupPreffix}", groupPreffix);
+ throw;
+ }
+ }
- public async ValueTask DeleteBulk(string group, params string[] keys)
+ public async ValueTask DeleteGroup(string groupPreffix)
{
- if (group.IsWhiteSpaceOrNull() || keys.AllWhiteSpaceOrNull())
+ try
{
+ var redisRslt = await GetGroupKeys(groupPreffix);
+ if (redisRslt is not null)
+ {
+ var keys = (RedisKey[])redisRslt!;
+ return keys.IsEmptyOrNull() || await _database.KeyDeleteAsync(keys) > 0;
+ }
return false;
}
- var rslt = true;
- foreach (var key in keys.Where(k => k.IsNotWhiteSpaceAndNull()).Select(k => (RedisValue)k.ToLowerInvariant()))
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "DeleteGroupRedisService-Exception: {GroupPreffix}", groupPreffix);
+ throw;
+ }
+ }
+ #endregion
+
+ #region Methods
+ private async ValueTask GetGroupKeys(string groupPreffix)
+ {
+ try
+ {
+ return groupPreffix.IsWhiteSpaceOrNull()
+ ? throw new BusinessException(code: ExceptionCode.BAD_REQUEST, message: BAD_REQUEST)
+ : await _database.ExecuteAsync("KEYS", $"{groupPreffix}*");
+ }
+ catch (Exception ex)
{
- rslt = rslt && await _database.HashDeleteAsync((RedisKey)group.ToLowerInvariant(), key);
+ _logger.LogError(ex, "GetGroupKeysRedisService-Exception: {GroupPreffix}", groupPreffix);
+ throw;
}
- return rslt;
}
#endregion
}
diff --git a/src/YANLib.Application.Redis/YANLib.Application.Redis.csproj b/src/YANLib.Application.Redis/YANLib.Application.Redis.csproj
index c62ff609..77079306 100644
--- a/src/YANLib.Application.Redis/YANLib.Application.Redis.csproj
+++ b/src/YANLib.Application.Redis/YANLib.Application.Redis.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/YANLib.Application.Redis/YANLibApplicationRedisModule.cs b/src/YANLib.Application.Redis/YANLibApplicationRedisModule.cs
index 137efc48..5ae61328 100644
--- a/src/YANLib.Application.Redis/YANLibApplicationRedisModule.cs
+++ b/src/YANLib.Application.Redis/YANLibApplicationRedisModule.cs
@@ -3,7 +3,8 @@
namespace YANLib.Application.Redis;
[DependsOn(
- typeof(YANLibApplicationContractsModule)
+ typeof(YANLibApplicationContractsModule),
+ typeof(YANLibDomainModule)
)]
public class YANLibApplicationRedisModule : AbpModule
{
diff --git a/src/YANLib.HttpApi/Controllers/YANJsonController.cs b/src/YANLib.HttpApi/Controllers/YANJsonController.cs
index eafc21ee..b9c55ca8 100644
--- a/src/YANLib.HttpApi/Controllers/YANJsonController.cs
+++ b/src/YANLib.HttpApi/Controllers/YANJsonController.cs
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
using Swashbuckle.AspNetCore.Annotations;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
@@ -15,17 +16,26 @@ namespace YANLib.Controllers;
public class YANJsonController : YANLibController
{
#region Fields
+ private readonly ILogger _logger;
private readonly IYANJsonService _service;
#endregion
#region Constructors
- public YANJsonController(IYANJsonService service) => _service = service;
+ public YANJsonController(ILogger logger, IYANJsonService service)
+ {
+ _logger = logger;
+ _service = service;
+ }
#endregion
#region Methods
[HttpGet("yan-vs-standards")]
[SwaggerOperation(Summary = "Deserialize speed test (YAN vs Standards)")]
- public async ValueTask YanVsStandards([Required] uint quantity = 10000, [Required] bool hideSystem = true) => Ok(await _service.YanVsStandards(quantity, hideSystem));
+ public async ValueTask YanVsStandards([Required] uint quantity = 10000, [Required] bool hideSystem = true)
+ {
+ _logger.LogInformation("YanVsStandardsYANJsonController: {Quantity}, {HideSystem}", quantity, hideSystem);
+ return Ok(await _service.YanVsStandards(quantity, hideSystem));
+ }
[HttpPost("serialize")]
[SwaggerOperation(Summary = "Serialize n-1 Pascal case")]