Azure Cosmos DB - SQL API Geo-Replication Using EF Core

Introduction

 
In the previous article, we have seen the introduction of Cosmos DB and EF Core with Cosmos DB-SQL API. If you haven’t read my previous articles then I highly encourage you to do so:
Azure CLI
 
In the previous article, we saw how to create Cosmos DB. You can refer to the article provided in the recap section.
  1. # Create resource group with name "GeoReplication"  
  2. $resourceGroup=“GeoReplication”  
  3. $location=“SouthIndia”  
  4. az group create -n $resourceGroup -l $location  
  5.   
  6. # App service plan created for the South India location  
  7. $name="AppServiceSI"  
  8. az appservice plan create -n $name   
  9. -g $resourceGroup --sku F1 -o table  
  10.   
  11. # App Service plan created for the East US location  
  12. $name="AppServiceEU"  
  13. az appservice plan create -n $name   
  14. -g $resourceGroup -l EastUs --sku F1   
  15. -o table  
  16.   
  17. # Create Application Insight in GeoReplication resource group  
  18. $name=“application insight  
  19. az resource create -g $resourceGroup  
  20. --resource-type "Microsoft.Insights/components"  
  21. -n $name -l $location -p '{"Application_Type":"web"}'  
  22.   
  23. # Displays Instrumentation key   
  24. az resource show -g $resourceGroup -n   
  25. $name --resource-type "Microsoft.Insights/components"  
  26. --query properties.InstrumentationKey  

Enabling Geo-Replication

 
Geo-replication can be enabled by clicking on “Enable Geo Replication”.
 
Azure Cosmos DB - SQL API Geo Replication Using EF Core
 
Multi-region write can be enabled by clicking on “Replicate Data Globally”
 
Azure Cosmos DB - SQL API Geo Replication Using EF Core
In this example, I have configured East US 2, North Europe and SouthEast Asia region for reads and writes.
 
Key benefits of Geo-replication
  • It increases the availability of the database at a given point of time. If one region is unavailable, other regions automatically handle application requests.
  • The application can perform near-real-time reads and writes against all the regions you chose for your database.
  • Application performance increases due to the availability of databases at multiple regions.

Coding

 
As compared to the previous article, I’m implementing the same program in Web API rather than the console App. I’ll not be explaining the code until it is absolutely necessary.
 
You can go through my previous article by referring to the recap section.
  1. public class Profile  
  2.     {  
  3.         public string Title { getset; }  
  4.         public string FirstName { getset; }  
  5.         public string LastName { getset; }  
  6.         public string DOB { getset; }  
  7.         public string PhoneNumber { getset; }  
  8.         public string Designation { getset; }  
  9.         public Company CurrentCompany { getset; }  
  10.         public string HighestEducation { getset; }  
  11.         public ICollection<Company> PreviousCompanies { getset; }  
  12.         public string UserId { getset; }  
  13.     }  
  14.    
  15. public class Company  
  16.     {  
  17.         public string CompanyName { getset; }  
  18.         public DateTime StartDate { getset; }  
  19.         public DateTime EndDate { getset; }  
  20.         public bool IsActive { getset; }  
  21.     }  
  22.    
  23.  public class ProfileContext:DbContext  
  24.     {  
  25.         public DbSet<Profile> Profiles { getset; }  
  26.    
  27.         public ProfileContext(DbContextOptions dbContextOptions):base(dbContextOptions)  
  28.         {  
  29.         }  
  30.    
  31.         protected override void OnModelCreating(ModelBuilder modelBuilder)  
  32.         {  
  33.             modelBuilder.ApplyConfiguration(new ProfileEntityConfiguration());  
  34.         }  
  35.     }  
  36.    
  37. public class ProfileEntityConfiguration : IEntityTypeConfiguration<Profile>  
  38.     {  
  39.         public void Configure(EntityTypeBuilder<Profile> builder)  
  40.         {  
  41.             builder.HasKey(x => x.UserId);  
  42.             builder.HasPartitionKey(x => x.UserId);  
  43.             builder.OwnsOne(x => x.CurrentCompany);  
  44.             builder.OwnsMany(x => x.PreviousCompanies);  
  45.         }  
  46.     }  
 Now, register the ProfileContext in the ConfigureServices method of Startup class
  1. var accountEndpoint = Configuration.GetValue<string>("ConnectionString:AccountEndpoint");  
  2.             var accountKey= Configuration.GetValue<string>("ConnectionString:AccountKey");  
  3.             var dbName= Configuration.GetValue<string>("ConnectionString:DatabaseName");  
  4. services.AddDbContext<ProfileContext>(x => x.UseCosmos(accountEndpoint, accountKey, dbName));  
  1. [ApiController]  
  2.     [Route("api/[controller]")]  
  3.     public class ProfileController:ControllerBase  
  4.     {  
  5.         private readonly ProfileContext profileContext;  
  6.         private readonly IConfiguration configuration;  
  7.    
  8.         public ProfileController(ProfileContext profileContext,IConfiguration configuration)  
  9.         {  
  10.             this.profileContext = profileContext;  
  11.             this.configuration = configuration;  
  12.             profileContext.Database.EnsureCreated();  
  13.         }  
  14.         [HttpGet]  
  15.         public ActionResult<IEnumerable<Profile>> GetAllProfiles()  
  16.         {  
  17.             return Ok(profileContext.Profiles.ToList());  
  18.         }  
  19.    
  20.         [HttpPost]  
  21.         public async Task<IActionResult> CreateProfile(Profile profile)  
  22.         {  
  23.             await profileContext.Profiles.AddAsync(profile);  
  24.             await profileContext.SaveChangesAsync();  
  25.             return Ok();  
  26.         }  
  27.     }  
Now run the application and it works! But wait, as we have configured geo-replication to which region of database instance the application is called? Good question.
 
To verify this we can enable Application Insight in our application by adding a nuget package Microsoft.ApplicationInsights.AspNetCore
 
Register Application Insight in the ConfigureServices method of Startup class.
  1. services.AddApplicationInsightsTelemetry();  
  1. "ApplicationInsights": {  
  2.         "InstrumentationKey""<Instrumentation Key here>"  
  3.     },  
 You can get the instrumentation key by executing below command
  1. # Displays Instrumentation key   
  2. az resource show -g $resourceGroup -n   
  3. $name --resource-type "Microsoft.Insights/components"  
  4. --query properties.InstrumentationKey  
Now deploy your application to Azure app service either to “AppServiceSI” or “AppServiceEU”. You can navigate to Application Insight by click on the resource group (GeoReplication) and now click on Application Map. Now precisely, you will be able to see on which database instance the query has been executed. 
 
As you know, we have 2 App Service
  • AppServiceSI- Deployed in South India
  • AppServiceEU- Deployed in East US
Now the application executes randomly on both the instances of the database. But is there a way to set the prefered location of the database? ex: App service deployed in South India should point to SouthEast Asia database region whereas, the app service deployed in East US should point to East US 2 database region because these database instances are geographically nearer to the app service location.
 
I would like to thank Julie Lerman, Jeremy Likness, and Smit Patel for giving the answer to configure the preferred location. I was struggling to get the answer and there are no articles/questions around preferred location anywhere.
 
 Azure Cosmos DB - SQL API Geo Replication Using EF Core
 
You can configure the preferred region in the AddDbContext method:
  1. public void ConfigureServices(IServiceCollection services)  
  2.        {  
  3.            var accountEndpoint = Configuration.GetValue<string>("ConnectionString:AccountEndpoint");  
  4.            var accountKey= Configuration.GetValue<string>("ConnectionString:AccountKey");  
  5.            var dbName= Configuration.GetValue<string>("ConnectionString:DatabaseName");  
  6.            var region = Configuration.GetValue<string>("ApiServerRegion");   
  7.            services.AddDbContext<ProfileContext>(x => x.UseCosmos(accountEndpoint, accountKey, dbName  
  8. ,cosmosOptionsAction=>cosmosOptionsAction.Region(region)));  
  9.            services.AddControllers();  
  10.            services.AddApplicationInsightsTelemetry();  
  11.        }  
 In the appsettings.json, add the below lines for East US App service whereas, set the ApiServerRegion=”SouthEastAsia” for the South Indian App service.
  1. "ApiServerRegion""EastUS2",  
Now deploy both the app service by setting the proper ApiServerRegion in the appsettings.json.
 
Now run both the app services. You will be able to see the result in the application map present in Application Insight.
 
I hope you like the article. In case you find the article as interesting then kindly like and share it.