/// <summary> /// Initializes a new instance of the <see cref="Sharding" /> class. /// <para>Bootstrap Elastic Scale by creating a new shard map manager and a shard map on the shard map manager database if necessary.</para> /// </summary> /// <param name="catalogConfig">The catalog configuration.</param> /// <param name="databaseConfig">The database configuration.</param> /// <param name="tenantsRepository">The tenants repository.</param> /// <param name="helper">The helper.</param> public Sharding(CatalogConfig catalogConfig, DatabaseConfig databaseConfig, ITenantsRepository tenantsRepository, IHelper helper) { try { _tenantsRepository = tenantsRepository; _helper = helper; var smmconnstr = _helper.GetSqlConnectionString(databaseConfig, catalogConfig); // Deploy shard map manager // if shard map manager exists, refresh content, else create it, then add content ShardMapManager smm; ShardMapManager = !ShardMapManagerFactory.TryGetSqlShardMapManager(smmconnstr, ShardMapManagerLoadPolicy.Lazy, out smm) ? ShardMapManagerFactory.CreateSqlShardMapManager(smmconnstr) : smm; // check if shard map exists and if not, create it ListShardMap <int> sm; ShardMap = !ShardMapManager.TryGetListShardMap(catalogConfig.CatalogDatabase, out sm) ? ShardMapManager.CreateListShardMap <int>(catalogConfig.CatalogDatabase) : sm; } catch (Exception ex) { // _logger.LogError(0, ex, "Error in sharding initialisation."); throw new ApplicationException("Error in sharding initialisation."); } }
/// <summary> /// Register tenant shard /// </summary> /// <param name="tenantServerConfig">The tenant server configuration.</param> /// <param name="databaseConfig">The database configuration.</param> /// <param name="catalogConfig">The catalog configuration.</param> /// <param name="resetEventDate">If set to true, the events dates for all tenants will be reset </param> public async void RegisterTenantShard(TenantServerConfig tenantServerConfig, DatabaseConfig databaseConfig, CatalogConfig catalogConfig, bool resetEventDate) { //get all database in devtenantserver var tenants = GetAllTenantNames(tenantServerConfig, databaseConfig); var connectionString = new SqlConnectionStringBuilder { UserID = databaseConfig.DatabaseUser, Password = databaseConfig.DatabasePassword, ApplicationName = "EntityFramework", ConnectTimeout = databaseConfig.ConnectionTimeOut }; Shard shard = Sharding.CreateNewShard(tenantServerConfig.TenantDatabase, tenantServerConfig.TenantServer, databaseConfig.DatabaseServerPort, catalogConfig.ServicePlan); foreach (var tenant in tenants) { var tenantId = GetTenantKey(tenant); var result = await Sharding.RegisterNewShard(tenantId, catalogConfig.ServicePlan, shard); if (result) { // resets all tenants' event dates if (resetEventDate) { #region EF6 //use EF6 since execution of Stored Procedure in EF Core for anonymous return type is not supported yet using (var context = new TenantContext(Sharding.ShardMap, tenantId, connectionString.ConnectionString)) { //context.Database.ExecuteSqlCommand("sp_ResetEventDates"); } #endregion #region EF core //https://github.com/aspnet/EntityFramework/issues/7032 //using (var context = new TenantDbContext(Sharding.ShardMap, tenantId, connectionString)) //{ // context.Database.ExecuteSqlCommand("sp_ResetEventDates"); //} #endregion } } } }
/// <summary> /// Registers the new shard. /// Verify if shard exists for the tenant. If not then create new shard and add tenant details to Tenants table in catalog /// </summary> /// <param name="tenantName">Name of the tenant.</param> /// <param name="tenantId">The tenant identifier.</param> /// <param name="tenantServerConfig">The tenant server configuration.</param> /// <param name="databaseConfig">The database configuration.</param> /// <param name="catalogConfig">The catalog configuration.</param> public static bool RegisterNewShard(string tenantName, int tenantId, TenantServerConfig tenantServerConfig, DatabaseConfig databaseConfig, CatalogConfig catalogConfig) { try { Shard shard; ShardLocation shardLocation = new ShardLocation(tenantServerConfig.TenantServer, tenantName, databaseConfig.SqlProtocol, databaseConfig.DatabaseServerPort); if (!ShardMap.TryGetShard(shardLocation, out shard)) { //create shard if it does not exist shard = ShardMap.CreateShard(shardLocation); } // Register the mapping of the tenant to the shard in the shard map. // After this step, DDR on the shard map can be used PointMapping <int> mapping; if (!ShardMap.TryGetMappingForKey(tenantId, out mapping)) { var pointMapping = ShardMap.CreatePointMapping(tenantId, shard); //convert from int to byte[] as tenantId has been set as byte[] in Tenants entity var key = _helper.ConvertIntKeyToBytesArray(pointMapping.Value); //add tenant to Tenants table var tenant = new Tenants { ServicePlan = catalogConfig.ServicePlan, TenantId = key, TenantName = tenantName }; _tenantsRepository.Add(tenant); } return(true); } catch (Exception ex) { // _logger.LogError(0, ex, "Error in registering new shard."); //throw new ApplicationException("Error in registering new shard."); return(false); } }
/// <summary> /// Register tenant shard /// </summary> /// <param name="tenantServerConfig">The tenant server configuration.</param> /// <param name="databaseConfig">The database configuration.</param> /// <param name="catalogConfig">The catalog configuration.</param> /// <param name="resetEventDate">If set to true, the events dates for all tenants will be reset </param> public async void RegisterTenantShard(TenantServerConfig tenantServerConfig, DatabaseConfig databaseConfig, CatalogConfig catalogConfig, bool resetEventDate) { //get all database in devtenantserver var tenants = GetAllTenantNames(tenantServerConfig, databaseConfig); var connectionString = new SqlConnectionStringBuilder { UserID = databaseConfig.DatabaseUser, Password = databaseConfig.DatabasePassword, ApplicationName = "EntityFramework", ConnectTimeout = databaseConfig.ConnectionTimeOut }; foreach (var tenant in tenants) { var tenantId = GetTenantKey(tenant); var result = await Sharding.RegisterNewShard(tenant, tenantId, tenantServerConfig.TenantServer, databaseConfig.DatabaseServerPort, catalogConfig.ServicePlan); if (result) { // resets all tenants' event dates if (resetEventDate) { #region EF6 try { //use EF6 since execution of Stored Procedure in EF Core for anonymous return type is not supported yet using (var context = new TenantContext(Sharding.ShardMap, tenantId, connectionString.ConnectionString)) { context.Database.ExecuteSqlCommand("sp_ResetEventDates"); } } catch (Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement.ShardManagementException ex) { string errorText; if (ex.ErrorCode == Microsoft.Azure.SqlDatabase.ElasticScale.ShardManagement.ShardManagementErrorCode.MappingIsOffline) { errorText = "Tenant '" + tenant + "' is offline. Could not reset event dates:" + ex.ToString(); } else { errorText = ex.ToString(); } Console.WriteLine(errorText); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } #endregion #region EF core //https://github.com/aspnet/EntityFramework/issues/7032 //using (var context = new TenantDbContext(Sharding.ShardMap, tenantId, connectionString)) //{ // context.Database.ExecuteSqlCommand("sp_ResetEventDates"); //} #endregion } } } }