Azure Cosmos DB-SQL API Geo Replication using EF Core- Part Three

Recap

In the previous articles, 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.

# Create resource group with name "GeoReplication"
$resourceGroup=“GeoReplication”
$location=“SouthIndia”
az group create -n $resourceGroup -l $location

# App service plan created for the South India location
$name="AppServiceSI"
az appservice plan create -n $name 
-g $resourceGroup --sku F1 -o table

# App Service plan created for the East US location
$name="AppServiceEU"
az appservice plan create -n $name 
-g $resourceGroup -l EastUs --sku F1 
-o table

# Create Application Insight in GeoReplication resource group
$name=“application insight
az resource create -g $resourceGroup
--resource-type "Microsoft.Insights/components" 
-n $name -l $location -p '{"Application_Type":"web"}'

# Displays Instrumentation key 
az resource show -g $resourceGroup -n 
$name --resource-type "Microsoft.Insights/components" 
--query properties.InstrumentationKey

Enabling Geo-Replication

Geo-replication can be enabled by clicking on “Enable Geo Replication”.

Multi-region write can be enabled by clicking on “Replicate Data Globally”

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 even given point of time. If one region is unavailable, other regions automatically handle application requests.
  • Application can perform near real-time reads and writes against all the regions you chose for your database.
  • Application performance increases due to availability of database at multiple region.

Coding

As compared to the previous article, I’m implementing the same program in Web API rather than console App. I’ll not be explaining the code until it is absolutely necessary. You can go through my previous article by referring to recap section.

public class Profile
    {
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string DOB { get; set; }
        public string PhoneNumber { get; set; }
        public string Designation { get; set; }
        public Company CurrentCompany { get; set; }
        public string HighestEducation { get; set; }
        public ICollection<Company> PreviousCompanies { get; set; }
        public string UserId { get; set; }
    }

public class Company
    {
        public string CompanyName { get; set; }
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
        public bool IsActive { get; set; }
    }

 public class ProfileContext:DbContext
    {
        public DbSet<Profile> Profiles { get; set; }

        public ProfileContext(DbContextOptions dbContextOptions):base(dbContextOptions)
        {
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.ApplyConfiguration(new ProfileEntityConfiguration());
        }
    }

public class ProfileEntityConfiguration : IEntityTypeConfiguration<Profile>
    {
        public void Configure(EntityTypeBuilder<Profile> builder)
        {
            builder.HasKey(x => x.UserId);
            builder.HasPartitionKey(x => x.UserId);
            builder.OwnsOne(x => x.CurrentCompany);
            builder.OwnsMany(x => x.PreviousCompanies);
        }
    }

Now, register the ProfileContext in the ConfigureServices method of Startup class

var accountEndpoint = Configuration.GetValue<string>("ConnectionString:AccountEndpoint");
            var accountKey= Configuration.GetValue<string>("ConnectionString:AccountKey");
            var dbName= Configuration.GetValue<string>("ConnectionString:DatabaseName");
services.AddDbContext<ProfileContext>(x => x.UseCosmos(accountEndpoint, accountKey, dbName));
[ApiController]
    [Route("api/[controller]")]
    public class ProfileController:ControllerBase
    {
        private readonly ProfileContext profileContext;
        private readonly IConfiguration configuration;

        public ProfileController(ProfileContext profileContext,IConfiguration configuration)
        {
            this.profileContext = profileContext;
            this.configuration = configuration;
            profileContext.Database.EnsureCreated();
        }
        [HttpGet]
        public ActionResult<IEnumerable<Profile>> GetAllProfiles()
        {
            return Ok(profileContext.Profiles.ToList());
        }

        [HttpPost]
        public async Task<IActionResult> CreateProfile(Profile profile)
        {
            await profileContext.Profiles.AddAsync(profile);
            await profileContext.SaveChangesAsync();
            return Ok();
        }
    }

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.

services.AddApplicationInsightsTelemetry();
"ApplicationInsights": {
        "InstrumentationKey": "<Instrumentation Key here>"
    },

You can get the instrumentation key by executing below command

# Displays Instrumentation key 
az resource show -g $resourceGroup -n 
$name --resource-type "Microsoft.Insights/components" 
--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 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 database. But is there a way to set 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 on configuring the preferred location. I was struggling to get the answer and there are no articles/questions around preferred location anywhere.

You can configure the preferred region in the AddDbContext method

 public void ConfigureServices(IServiceCollection services)
        {
            var accountEndpoint = Configuration.GetValue<string>("ConnectionString:AccountEndpoint");
            var accountKey= Configuration.GetValue<string>("ConnectionString:AccountKey");
            var dbName= Configuration.GetValue<string>("ConnectionString:DatabaseName");
            var region = Configuration.GetValue<string>("ApiServerRegion"); 
            services.AddDbContext<ProfileContext>(x => x.UseCosmos(accountEndpoint, accountKey, dbName
 ,cosmosOptionsAction=>cosmosOptionsAction.Region(region)));
            services.AddControllers();
            services.AddApplicationInsightsTelemetry();
        }

In the appsettings.json, add the below lines for East US App service whereas, set the ApiServerRegion=”SouthEastAsia” for the South Indian App service.

"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.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s