Learn how to implement multi-cluster management to distribute and manage your data across several machines.
Multi-cluster management is deprecated and will be sunset.
If you have issues with your Algolia infrastructure due to large volumes of data,
contact the Algolia support team.
When your data no longer fits on a single machine, you have to add new clusters. Multi-Cluster Management (MCM) simplifies this process by letting you distribute and manage your data across several machines.For example, a music streaming application lets users create public and private playlists.
With MCM, the number of playlists can grow without fearing they might exceed your current cluster’s size limit.
You also get dedicated user access out of the box.
Without MCM, an application is bound to a single cluster. It means that splitting data across several clusters requires you to assign data to a specific application ID, so you can manually maintain and orchestrate the distribution by yourself.With MCM, all clusters use the same application ID.
MCM keeps a mapping on each cluster to route requests to the correct cluster.
All you need to do is assign a cluster to a user with the assignUserId method.
var response = await client.AssignUserIdAsync( "user42", new AssignUserIdParams { Cluster = "d4242-eu" });
Without MCM, every record needs a userID attribute to tag the right user. Additionally, you need to set the userID attribute as a filter at query time.
namespace Algolia;using System;using System.Collections.Generic;using System.Net.Http;using System.Text.Json;using Algolia.Search.Clients;using Algolia.Search.Http;using Algolia.Search.Models.Search;class SetSettingsThenSaveObjects{ private static readonly List> playlists = []; // Your records private static string GetAppIdFor(string user) { return ""; // Implement your own logic here } private static string GetIndexingApiKeyFor(string user) { return ""; // Implement your own logic here } async Task Main(string[] args) { foreach (var playlist in playlists) { // Fetch from your own data storage and with your own code // the associated application ID and API key for this user var appId = GetAppIdFor((playlist.GetValueOrDefault("user", "") as string)!); var apiKey = GetIndexingApiKeyFor((playlist.GetValueOrDefault("user", "") as string)!); try { var client = new SearchClient(new SearchConfig(appId, apiKey)); var settings = new IndexSettings { AttributesForFaceting = ["searchable(playlistName)"] }; await client.SetSettingsAsync("", settings); await client.SaveObjectsAsync("", playlists); } catch (Exception e) { Console.WriteLine(e.Message); } } }}
With MCM, all records are automatically tagged using the X-Algolia-User-ID header you send at query time. The engine automatically adds an extra attribute __userID__ inside records to identify the userID associated with each record.
namespace Algolia;using System;using System.Collections.Generic;using System.Net.Http;using System.Text.Json;using Algolia.Search.Clients;using Algolia.Search.Http;using Algolia.Search.Models.Search;class SetHeaderUserIDThenSaveObjects{ private static readonly List> playlists = []; // Your records async Task Main(string[] args) { var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")); foreach (var playlist in playlists) { try { var playlistUserID = playlist.GetValueOrDefault("userID", "") as string; await client.SaveObjectsAsync( "", playlists, false, 1000, new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", playlistUserID).Build() ); } catch (Exception e) { Console.WriteLine(e.Message); } } }}
For simplicity’s sake, the preceding snippets add records one by one.
For better performance, you should group your records by userID first, then batch them.
Without MCM, you need to tag every public record with a particular value (such as “public”) for the userID attribute. Then, you need to filter on that value to search for public records.
namespace Algolia;using System;using System.Collections.Generic;using System.Net.Http;using System.Text.Json;using Algolia.Search.Clients;using Algolia.Search.Http;using Algolia.Search.Models.Search;class SaveObjectsMcm{ private readonly List> playlists = []; // Your records private static List> GetAllAppIdConfigurations() { return [ /* A list of your MCM AppID/ApiKey pairs */ ]; } async Task Main(string[] args) { // Fetch from your own data storage and with your own code // the list of application IDs and API keys to target each cluster var configurations = GetAllAppIdConfigurations(); // Send the records to each cluster foreach (var config in configurations) { try { var client = new SearchClient( new SearchConfig( config.GetValueOrDefault("appID", ""), config.GetValueOrDefault("apiKey", "") ) ); await client.SaveObjectsAsync("", playlists); } catch (Exception e) { Console.WriteLine(e.Message); } } }}
With MCM, you can use the userID value * to flag records as public, allowing all users to see them. Public records are automatically replicated on all clusters to avoid network latency during search.
namespace Algolia;using System;using System.Collections.Generic;using System.Net.Http;using System.Text.Json;using Algolia.Search.Clients;using Algolia.Search.Http;using Algolia.Search.Models.Search;class SaveObjectsPublicUser{ private static readonly List> playlists = []; // Your records async Task Main(string[] args) { var client = new SearchClient(new SearchConfig("ALGOLIA_APPLICATION_ID", "ALGOLIA_API_KEY")); await client.SaveObjectsAsync( "", playlists, false, 1000, new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", "*").Build() ); }}
Without MCM, you need to manually handle the filtering logic to filter on private data for a specific user and public data.
namespace Algolia;using System;using System.Collections.Generic;using System.Net.Http;using System.Text.Json;using Algolia.Search.Clients;using Algolia.Search.Http;using Algolia.Search.Models.Search;class McmSearchWithout{ private static string GetAppIdFor(string user) { return ""; // Implement your own logic here } private static string GetIndexingApiKeyFor(string user) { return ""; // Implement your own logic here } async Task Main(string[] args) { // Fetch from your own data storage and with your own code // the associated application ID and API key for this user var appId = GetAppIdFor("user42"); var apiKey = GetIndexingApiKeyFor("user42"); var client = new SearchClient(new SearchConfig(appId, apiKey)); var searchParams = new SearchParams( new SearchParamsObject { Query = "", FacetFilters = new FacetFilters( [new FacetFilters("user:user42"), new FacetFilters("user:public")] ), } ); await client.SearchSingleIndexAsync("", searchParams); }}
With MCM, all the engine needs is the userID (with the X-Algolia-User-ID header) that the query targets, so it can route the request to the right cluster. It automatically adds the right filters to retrieve both the user’s data and public data.
var response = await client.SearchSingleIndexAsync( "INDEX_NAME", new SearchParams(new SearchParamsObject { Query = "peace" }), new RequestOptionBuilder().AddExtraHeader("X-Algolia-User-ID", "user42").Build());
You can’t rely on filters to select what data to return to a specific user because users can change these parameters.
If all you have to pick specific data is query time filters, anyone can alter these to access the data of another user.To properly restrict access of a user to only their data (and the public data), you need to use secured API keys.Without MCM, you need to set the filters to authorize manually.
var response = client.GenerateSecuredApiKey( "ALGOLIA_SEARCH_API_KEY", new SecuredApiKeyRestrictions { Filters = "user:user42 AND user:public" });
With MCM, you still need to generate secured API keys, but all you have to do is provide the userToken. It automatically adds the right filters to authorize.
var response = client.GenerateSecuredApiKey( "ALGOLIA_SEARCH_API_KEY", new SecuredApiKeyRestrictions { UserToken = "user42" });
With MCM, you always need to provide the X-Algolia-User-ID header, even when the query contains the userToken parameter.
The header is required to route the requests efficiently.
When performing actions that have global impact on all clusters, such as using addApiKey or setSettings, you don’t need to provide the X-Algolia-User-ID header.