using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.Concurrent; using Newtonsoft.Json; using Amazon.Runtime; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.DocumentModel; using Amazon.DynamoDBv2.Model; using Axion.Collections.Concurrent; using DYNAMO_DB_TABLE_NAME = System.String; using DYNAMO_DB_TABLE_FULL_NAME = System.String; using MongoDB.Driver.Core.Configuration; namespace ServerCore; public abstract class DynamoDbConnectorBase { // 하나의 아이템 최대 저장 크기 : PK + SK + ITEM <= ITEM_MAX_SIZE_BYTES public static readonly UInt32 ITEM_MAX_SIZE_BYTES = 400 * 1024; private AmazonDynamoDBClient? m_db_client; private ConcurrentDictionary m_to_load_table_names = new(); private string m_access_key = string.Empty; private string m_secret_key = string.Empty; private AmazonDynamoDBConfig? m_db_config; private ConcurrentDictionary m_loaded_tables = new(); public bool connectToDb(string accessKey, string secretKey, AmazonDynamoDBConfig dbConfig) { m_access_key = accessKey; m_secret_key = secretKey; m_db_config = dbConfig; try { m_db_client = new AmazonDynamoDBClient(accessKey, secretKey, dbConfig); } catch (Exception e) { Log.getLogger().error($"Exception !!!, Failed to perform in DynamoDbConnectorBase.connectToDb() !!! : exception:{e}, AccessKey:{accessKey}, SecretKey:{secretKey} - {toBasicString()}"); return false; } return true; } public async Task isExistTable(string tableName) { try { if (m_db_client == null) { return false; } var loaded_tables = await m_db_client.ListTablesAsync(); return loaded_tables.TableNames.Contains(tableName); } catch (Exception e) { Log.getLogger().fatal($"Exception !!!, Failed to perform in DynamoDbConnectorBase.isExistTable() !!! : exception:{e}, toFindTableName{tableName}"); return false; } } public static async Task createTable( AmazonDynamoDBClient dbClient, DYNAMO_DB_TABLE_FULL_NAME tableFullName , UpdateTimeToLiveRequest ttlUpdate , List tableAttributes , List tableKeySchema ) { var request = new CreateTableRequest { TableName = tableFullName, AttributeDefinitions = tableAttributes, KeySchema = tableKeySchema, BillingMode = BillingMode.PAY_PER_REQUEST }; try { var new_table = await dbClient.CreateTableAsync(request); Log.getLogger().info($"Created DynamoDB Table : {new_table.TableDescription.TableName}"); var ttl_res = await dbClient.UpdateTimeToLiveAsync(ttlUpdate); if (System.Net.HttpStatusCode.OK != ttl_res.HttpStatusCode) { Log.getLogger().fatal($"Failed to UpdateTimeToLiveAsync() !!!, not configured TTL : {ttlUpdate.TimeToLiveSpecification.AttributeName}, toCreateTableName{tableFullName}"); return false; } var ttl_status_res = await dbClient.DescribeTimeToLiveAsync(new DescribeTimeToLiveRequest { TableName = tableFullName }); if (TimeToLiveStatus.ENABLED != ttl_status_res.TimeToLiveDescription.TimeToLiveStatus) { Log.getLogger().fatal($"Failed to check configured TTL !!! : ttlName:{ttlUpdate.TimeToLiveSpecification.AttributeName}, toCreateTableName{tableFullName}"); return false; } } catch (Exception e) { Log.getLogger().fatal($"Exception !!!, Failed to perform in DynamoDbConnectorBase.createTable() !!! : exception:{e}, toCreateTableName{tableFullName}"); return false; } return true; } public static bool checkItemSizeLimit(string itemString) { var bytes = Encoding.UTF8.GetBytes(itemString); if (bytes.Length > ITEM_MAX_SIZE_BYTES) { return false; } return true; } public async Task createTable( DYNAMO_DB_TABLE_FULL_NAME tableFullName , List tableAttributes , List tableKeySchema , List globalSecondaryIndexes) { var request = new CreateTableRequest { TableName = tableFullName, AttributeDefinitions = tableAttributes, KeySchema = tableKeySchema, GlobalSecondaryIndexes = globalSecondaryIndexes, BillingMode = BillingMode.PAY_PER_REQUEST }; try { if (m_db_client == null) { return false; } var new_table = await m_db_client.CreateTableAsync(request); Log.getLogger().info($"Created DynamoDB Table : {new_table.TableDescription.TableName}"); } catch (Exception e) { Log.getLogger().fatal($"Exception !!!, Failed to perform in DynamoDbConnectorBase.createTable() !!! : exception:{e}, toCreateTableName{tableFullName}"); return false; } return true; } public async Task deleteTables(List tableFullNames) { foreach(var table_full_name in tableFullNames) { var request = new DeleteTableRequest { TableName = table_full_name, }; try { if (m_db_client == null) { return false; } var deleted_table = await m_db_client.DeleteTableAsync(request); } catch (Exception e) { Log.getLogger().fatal($"Exception !!!, Failed to perform in DynamoDbConnectorBase.deleteTables() !!! : exception:{e}, toDeleteTableName{table_full_name}"); return false; } } return true; } public async Task getTableDescription(DYNAMO_DB_TABLE_FULL_NAME tableFullName) { try { if (m_db_client == null) { return null; } var response = await m_db_client.DescribeTableAsync(tableFullName); return response.Table; } catch (Exception e) { Log.getLogger().fatal($"Exception !!!, Failed to perform in DynamoDbConnectorBase.getTableDescription() !!! : exception:{e}, TableName{tableFullName}"); return null; } } public Table getTableByName(DYNAMO_DB_TABLE_NAME tableName) { m_to_load_table_names.TryGetValue(tableName, out var table_full_name); NullReferenceCheckHelper.throwIfNull(table_full_name, () => $"table_full_name is null !!! : tableName:{tableName}"); m_loaded_tables.TryGetValue(table_full_name, out var table); NullReferenceCheckHelper.throwIfNull(table, () => $"table is null !!!, Not found DynamoDb Table !!! : tableFullName:{table_full_name}"); return table; } public Table getTableByFullName(DYNAMO_DB_TABLE_FULL_NAME tableFullName) { m_loaded_tables.TryGetValue(tableFullName, out var table); NullReferenceCheckHelper.throwIfNull(table, () => $"table is null !!!, Not found DynamoDb Table !!! : tableFullName:{tableFullName}"); return table; } public bool addTable(DYNAMO_DB_TABLE_FULL_NAME tableFullName, Table loadedTable) { return m_loaded_tables.TryAdd(tableFullName, loadedTable); } public bool hasTableName(DYNAMO_DB_TABLE_NAME tableName) => m_to_load_table_names.ContainsKey(tableName); public DYNAMO_DB_TABLE_FULL_NAME getTableFullName(DYNAMO_DB_TABLE_NAME tableName) { m_to_load_table_names.TryGetValue(tableName, out var table_full_name); NullReferenceCheckHelper.throwIfNull(table_full_name, () => $"table_full_name is null !!! : tableName:{tableName}"); return table_full_name; } public ConcurrentDictionary getTableNames() => m_to_load_table_names; protected bool addTableFullName(DYNAMO_DB_TABLE_NAME tableName, DYNAMO_DB_TABLE_FULL_NAME tableFullName) => m_to_load_table_names.TryAdd(tableName, tableFullName); public string toConnectionKeyString() { return $"AccessKey:{m_access_key}, SecretKey:{m_secret_key}"; } public string toBasicString() { return $"ServerUrl:{m_db_config?.ServiceURL}, RegionEndPoint:{m_db_config?.RegionEndpoint}"; } public AmazonDynamoDBClient? getDbClient() => m_db_client; }