Use follower databases

The follower database feature allows you to attach a database located in a different cluster to your Azure Data Explorer cluster. The follower database is attached in read-only mode, making it possible to view the data and run queries on the data that was ingested into the leader database. The follower database synchronizes changes in the leader databases. Because of the synchronization, there's a data lag of a few seconds to a few minutes in data availability. The length of the time lag depends on the overall size of the leader database metadata. The leader and follower databases use the same storage account to fetch the data. The storage is owned by the leader database. The follower database views the data without needing to ingest it. Since the attached database is a read-only database, the data, tables, and policies in the database can't be modified except for caching policy, principals, and permissions. Attached databases can't be deleted. They must be detached by the leader or follower and only then they can be deleted.

Attaching a database to a different cluster using the follower capability is used as the infrastructure to share data between organizations and teams. The feature is useful to segregate compute resources to protect a production environment from non-production use cases. Follower can also be used to associate the cost of Azure Data Explorer cluster to the party that runs queries on the data.

For code samples based on previous SDK versions, see the archived article.

Which databases are followed?

  • A cluster can follow one database, several databases, or all databases of a leader cluster.
  • A single cluster can follow databases from multiple leader clusters.
  • A cluster can contain both follower databases and leader databases.

Prerequisites

Attach a database

There are various methods you can use to attach a database. In this article, we discuss attaching a database using C#, Python, PowerShell, or an Azure Resource Manager template. To attach a database, you must have user, group, service principal, or managed identity with at least contributor role on the leader cluster and the follower cluster. Add or remove role assignments using Azure portal, PowerShell, Azure CLI, and ARM template. Learn more about Azure role-based access control (Azure RBAC) and the different roles.

Table level sharing

When attaching the database all tables, external tables and materialized views are followed as well. You can share specific tables/external tables/materialized views by configuring the 'TableLevelSharingProperties'.

'TableLevelSharingProperties' contains eight arrays of strings: tablesToInclude, tablesToExclude, externalTablesToInclude, externalTablesToExclude, materializedViewsToInclude, materializedViewsToExclude, functionsToInclude, and functionsToExclude. The maximum number of entries in all arrays together is 100.

Note

  • Table level sharing is not supported when using '*' all databases notation.
  • When materialized views are included, their source tables are included as well.

Examples

  1. Include all tables. No '*' is needed, since all tables are followed by default:

    tablesToInclude = []
    
  2. Include all tables with names that start with "Logs":

    tablesToInclude = ["Logs*"]
    
  3. Exclude all external tables:

    externalTablesToExclude = ["*"]
    
  4. Exclude all materialized views:

    materializedViewsToExclude=["*"]
    

Database name override

You can optionally make the database name in the follower cluster different from the leader cluster. For example, you may want to attach the same database name from multiple leader clusters to a follower cluster. To specify a different database name, configure the 'DatabaseNameOverride' or 'DatabaseNamePrefix' property.

Attach a database using C#

Required NuGet packages

C# example

var tenantId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx"; //Directory (tenant) ID
var clientId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx"; //Application ID
var clientSecret = "PlaceholderClientSecret"; //Client Secret
var followerSubscriptionId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx";
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
var resourceManagementClient = new ArmClient(credentials, followerSubscriptionId);
var followerResourceGroupName = "followerResourceGroup";
var followerClusterName = "follower";
var subscription = await resourceManagementClient.GetDefaultSubscriptionAsync();
var resourceGroup = (await subscription.GetResourceGroupAsync(followerResourceGroupName)).Value;
var cluster = (await resourceGroup.GetKustoClusterAsync(followerClusterName)).Value;
var attachedDatabaseConfigurations = cluster.GetKustoAttachedDatabaseConfigurations();
var attachedDatabaseConfigurationName = "attachedDatabaseConfiguration"
var leaderSubscriptionId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx";
var leaderResourceGroup = "leaderResourceGroup";
var leaderClusterName = "leader";
var attachedDatabaseConfigurationData = new KustoAttachedDatabaseConfigurationData
{
    ClusterResourceId = new ResourceIdentifier($"/subscriptions/{leaderSubscriptionId}/resourceGroups/{leaderResourceGroup}/providers/Microsoft.Kusto/Clusters/{leaderClusterName}"),
    DatabaseName = "<databaseName>", // Can be a specific database name in a leader cluster or * for all databases
    DefaultPrincipalsModificationKind = KustoDatabaseDefaultPrincipalsModificationKind.Union,
    Location = AzureLocation.NorthCentralUS
};
// Table level sharing properties are not supported when using '*' all databases notation.
if (attachedDatabaseConfigurationData.DatabaseName != "*")
{
    // Set up the table level sharing properties - the following is just an example.
    attachedDatabaseConfigurationData.TableLevelSharingProperties = new KustoDatabaseTableLevelSharingProperties();
    attachedDatabaseConfigurationData.TableLevelSharingProperties.TablesToInclude.Add("table1");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.TablesToExclude.Add("table2");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.ExternalTablesToExclude.Add("exTable1");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.ExternalTablesToInclude.Add("exTable2");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.MaterializedViewsToInclude.Add("matTable1");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.MaterializedViewsToExclude.Add("matTable2");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.FunctionsToInclude.Add("func1");
    attachedDatabaseConfigurationData.TableLevelSharingProperties.FunctionsToExclude.Add("func2");
}
await attachedDatabaseConfigurations.CreateOrUpdateAsync(WaitUntil.Completed, attachedDatabaseConfigurationName, attachedDatabaseConfigurationData);

Verify that the database was successfully attached

To verify that the database was successfully attached, find your attached databases in the Azure portal. You can verify the databases were successfully attached in either the follower or leader clusters.

Check your follower cluster

  1. Browse to the follower cluster and select Databases.

  2. In the database list, search for new read-only databases.

    Screenshot of read-only follower databases in portal.

    You can also view this list in the database overview page:

    Screenshot of databases overview page with list of follower clusters.

Check your leader cluster

  1. Browse to the leader cluster and select Databases

  2. Check that the relevant databases are marked as SHARED WITH OTHERS > Yes

  3. Toggle the relationship link to view details.

    Screenshot of databases shared with others to check leader cluster.

    You can also view this in the database overview page:

    Screenshot of overview with list of databases shared with others.

Detach the follower database

Note

To detach a database from the follower or leader side, you must have user, group, service principal, or managed identity with at least contributor role on the cluster from which you are detaching the database. In the example below, we use service principal.

Detach the attached follower database from the follower cluster using C#**

The follower cluster can detach any attached follower database as follows:

var tenantId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx"; //Directory (tenant) ID
var clientId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx"; //Application ID
var clientSecret = "PlaceholderClientSecret"; //Client Secret
var followerSubscriptionId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx";
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
var resourceManagementClient = new ArmClient(credentials, followerSubscriptionId);
var followerResourceGroupName = "testrg";
//The cluster and database attached database configuration are created as part of the prerequisites
var followerClusterName = "follower";
var attachedDatabaseConfigurationsName = "attachedDatabaseConfiguration";
var subscription = await resourceManagementClient.GetDefaultSubscriptionAsync();
var resourceGroup = (await subscription.GetResourceGroupAsync(followerResourceGroupName)).Value;
var cluster = (await resourceGroup.GetKustoClusterAsync(followerClusterName)).Value;
var attachedDatabaseConfiguration = (await cluster.GetKustoAttachedDatabaseConfigurationAsync(attachedDatabaseConfigurationsName)).Value;
await attachedDatabaseConfiguration.DeleteAsync(WaitUntil.Completed);

Detach the attached follower database from the leader cluster using C#

The leader cluster can detach any attached database as follows:

var tenantId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx"; //Directory (tenant) ID
var clientId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx"; //Application ID
var clientSecret = "PlaceholderClientSecret"; //Client Secret
var leaderSubscriptionId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx";
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
var resourceManagementClient = new ArmClient(credentials, leaderSubscriptionId);
var leaderResourceGroupName = "testrg";
var leaderClusterName = "leader";
var subscription = await resourceManagementClient.GetDefaultSubscriptionAsync();
var resourceGroup = (await subscription.GetResourceGroupAsync(leaderResourceGroupName)).Value;
var cluster = (await resourceGroup.GetKustoClusterAsync(leaderClusterName)).Value;
var followerSubscriptionId = "xxxxxxxx-xxxxx-xxxx-xxxx-xxxxxxxxx";
var followerResourceGroupName = "followerResourceGroup";
//The cluster and attached database configuration that are created as part of the Prerequisites
var followerClusterName = "follower";
var attachedDatabaseConfigurationsName = "attachedDatabaseConfiguration";
var followerDatabaseDefinition = new KustoFollowerDatabaseDefinition(
    clusterResourceId: new ResourceIdentifier($"/subscriptions/{followerSubscriptionId}/resourceGroups/{followerResourceGroupName}/providers/Microsoft.Kusto/Clusters/{followerClusterName}"),
    attachedDatabaseConfigurationName: attachedDatabaseConfigurationsName
);
await cluster.DetachFollowerDatabasesAsync(WaitUntil.Completed, followerDatabaseDefinition);

Manage principals, permissions, and caching policy

Manage principals

When attaching a database, specify the "default principals modification kind". The default is to combine the override authorized principals with the leader database collection of authorized principals

Kind Description
Union The attached database principals will always include the original database principals plus other new principals added to the follower database.
Replace No inheritance of principals from the original database. New principals must be created for the attached database.
None The attached database principals include only the principals of the original database with no other principals.

For more information about using management commands to configure the authorized principals, see Management commands for managing a follower cluster.

Manage permissions

Managing read-only database permission is the same as for all database types. To assign permissions, see Manage database permissions in the Azure portal or use management commands to Manage database security roles.

Configure caching policy

The follower database administrator can modify the caching policy of the attached database or any of its tables on the hosting cluster. The default is to combine the source database in the leader cluster database and table-level caching policies with the policies defined in the database and table-level override policies. You can, for example, have a 30 day caching policy on the leader database for running monthly reporting and a three day caching policy on the follower database to query only the recent data for troubleshooting. For more information about using management commands to configure the caching policy on the follower database or table, see Management commands for managing a follower cluster.

Notes

  • If there are conflicts between databases of leader/follower clusters, when all databases are followed by the follower cluster, they're resolved as follows:
    • A database named DB created on the follower cluster takes precedence over a database with the same name that was created on the leader cluster. That's why database DB in the follower cluster needs to be removed or renamed for the follower cluster to include the leader's database DB.
    • A database named DB followed from two or more leader clusters will be arbitrarily chosen from one of the leader clusters, and won't be followed more than once.
  • Commands for showing cluster activity log and history run on a follower cluster will show the activity and history on the follower cluster, and their result sets won't include those results of the leader cluster or clusters.
    • For example: a .show queries command run on the follower cluster will only show queries run on databases followed by follower cluster, and not queries run against the same database in the leader cluster.

Limitations

  • The follower and the leader clusters must be in the same region.
  • If Streaming ingestion is used on a database that is being followed, the follower cluster should be enabled for Streaming Ingestion to allow following of streaming ingestion data.
  • Following a cluster with data encryption using customer managed keys (CMK) is supported with the following limitations:
    • Neither the follower cluster nor the leader cluster is following other clusters.
    • If a follower cluster is following a leader cluster with CMK enabled, and the leader's access to the key is revoked, both the leader and the follower clusters will be suspended. In this situation, you can either resolve the CMK issue and then resume the follower cluster, or you can detach the follower databases from the follower cluster and resume independent of the leader cluster.
  • You can't delete a database that is attached to a different cluster before detaching it.
  • You can't delete a cluster that has a database attached to a different cluster before detaching it.
  • Table level sharing properties aren't supported when following all databases.
  • In follower databases, to query external tables that use a Managed Identity as the authentication method, the Managed Identity must be added to the follower cluster. This capability doesn't work when the leader and follower clusters are provisioned in different tenants.

Next step