Skip to content

Commit b753c3e

Browse files
committed
Merged PR 747603: Allow BuildXL to use multiple Blob L3 shards
1 parent 7256583 commit b753c3e

File tree

2 files changed

+41
-63
lines changed

2 files changed

+41
-63
lines changed

Public/Src/Cache/VerticalStore/MemoizationStoreAdapter/BlobCacheFactory.cs

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
using System.ComponentModel;
77
using System.Diagnostics.CodeAnalysis;
88
using System.Diagnostics.ContractsLight;
9+
using System.Linq;
910
using System.Threading.Tasks;
1011
using BuildXL.Cache.ContentStore.Distributed.Blob;
12+
using BuildXL.Cache.ContentStore.Interfaces.Auth;
1113
using BuildXL.Cache.Interfaces;
1214
using BuildXL.Cache.MemoizationStore.Distributed.Stores;
13-
using BuildXL.Utilities.Core;
15+
using BuildXL.Utilities.Collections;
1416
using BuildXL.Utilities.Configuration;
17+
using BuildXL.Utilities.Core;
1518
using AbsolutePath = BuildXL.Cache.ContentStore.Interfaces.FileSystem.AbsolutePath;
16-
using System.Security.Principal;
17-
using BuildXL.Cache.ContentStore.Interfaces.Auth;
1819

1920
namespace BuildXL.Cache.MemoizationStoreAdapter
2021
{
@@ -28,7 +29,7 @@ namespace BuildXL.Cache.MemoizationStoreAdapter
2829
/// Current limitations while we flesh things out:
2930
/// 1) APIs around tracking named sessions are not implemented
3031
/// </remarks>
31-
public partial class BlobCacheFactory : ICacheFactory
32+
public class BlobCacheFactory : ICacheFactory
3233
{
3334
/// <summary>
3435
/// Inheritable configuration settings for cache factories that wish to configure a connection to a blob cache
@@ -76,6 +77,15 @@ public abstract class BlobCacheConfig
7677
/// </remarks>
7778
[DefaultValue(0)]
7879
public int RetentionPolicyInDays { get; set; }
80+
81+
/// <nodoc />
82+
[DefaultValue("default")]
83+
public string Universe { get; set; }
84+
85+
/// <nodoc />
86+
[DefaultValue("default")]
87+
public string Namespace { get; set; }
88+
7989
}
8090

8191
/// <summary>
@@ -89,14 +99,6 @@ public sealed class Config : BlobCacheConfig
8999
[DefaultValue(typeof(CacheId))]
90100
public CacheId CacheId { get; set; }
91101

92-
/// <nodoc />
93-
[DefaultValue("default")]
94-
public string Universe { get; set; }
95-
96-
/// <nodoc />
97-
[DefaultValue("default")]
98-
public string Namespace { get; set; }
99-
100102
/// <summary>
101103
/// Path to the log file for the cache.
102104
/// </summary>
@@ -157,7 +159,7 @@ public async Task<Possible<ICache, Failure>> InitializeCacheAsync(Config configu
157159
logger: new DisposeLogger(() => new EtwFileLog(logPath.Path, configuration.CacheId), configuration.LogFlushIntervalSeconds),
158160
statsFile: new AbsolutePath(logPath.Path + ".stats"),
159161
precedingStateDegradationFailures: failures);
160-
162+
161163
var startupResult = await cache.StartupAsync();
162164
if (!startupResult.Succeeded)
163165
{
@@ -172,14 +174,12 @@ public async Task<Possible<ICache, Failure>> InitializeCacheAsync(Config configu
172174
}
173175
}
174176

175-
private static MemoizationStore.Interfaces.Caches.ICache CreateCache(Config configuration)
177+
internal static MemoizationStore.Interfaces.Caches.IFullCache CreateCache(BlobCacheConfig configuration)
176178
{
177-
IAzureStorageCredentials credentials = GetAzureCredentialsFromBlobFactoryConfig(configuration);
178-
179-
var accountName = BlobCacheStorageAccountName.Parse(credentials.GetAccountName());
179+
var credentials = LoadAzureCredentials(configuration);
180180

181181
var factoryConfiguration = new AzureBlobStorageCacheFactory.Configuration(
182-
ShardingScheme: new ShardingScheme(ShardingAlgorithm.SingleShard, new List<BlobCacheStorageAccountName> { accountName }),
182+
ShardingScheme: new ShardingScheme(ShardingAlgorithm.JumpHash, credentials.Keys.ToList()),
183183
Universe: configuration.Universe,
184184
Namespace: configuration.Namespace,
185185
RetentionPolicyInDays: configuration.RetentionPolicyInDays);
@@ -188,14 +188,22 @@ private static MemoizationStore.Interfaces.Caches.ICache CreateCache(Config conf
188188
}
189189

190190
/// <nodoc />
191-
internal static IAzureStorageCredentials GetAzureCredentialsFromBlobFactoryConfig(BlobCacheConfig configuration)
191+
internal static Dictionary<BlobCacheStorageAccountName, IAzureStorageCredentials> LoadAzureCredentials(BlobCacheConfig configuration)
192192
{
193-
IAzureStorageCredentials credentials;
193+
var credentials = new Dictionary<BlobCacheStorageAccountName, IAzureStorageCredentials>();
194194
var connectionString = Environment.GetEnvironmentVariable(configuration.ConnectionStringEnvironmentVariableName);
195195

196196
if (!string.IsNullOrEmpty(connectionString))
197197
{
198-
credentials = new SecretBasedAzureStorageCredentials(connectionString);
198+
credentials.AddRange(
199+
connectionString.Split(' ')
200+
.Select(
201+
secret =>
202+
{
203+
var credential = new SecretBasedAzureStorageCredentials(secret.Trim());
204+
var accountName = BlobCacheStorageAccountName.Parse(credential.GetAccountName());
205+
return new KeyValuePair<BlobCacheStorageAccountName, IAzureStorageCredentials>(accountName, credential);
206+
}));
199207
}
200208
else if (configuration.ManagedIdentityId is not null && configuration.StorageAccountEndpoint is not null)
201209
{
@@ -207,7 +215,8 @@ internal static IAzureStorageCredentials GetAzureCredentialsFromBlobFactoryConfi
207215
throw new InvalidOperationException($"'{configuration.StorageAccountEndpoint}' does not represent a valid URI.");
208216
}
209217

210-
credentials = new ManagedIdentityAzureStorageCredentials(configuration.ManagedIdentityId, uri);
218+
var credential = new ManagedIdentityAzureStorageCredentials(configuration.ManagedIdentityId, uri);
219+
credentials.Add(BlobCacheStorageAccountName.Parse(credential.GetAccountName()), credential);
211220
}
212221
else
213222
{

Public/Src/Cache/VerticalStore/MemoizationStoreAdapter/EphemeralCacheFactory.cs

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,18 @@
66
using System.ComponentModel;
77
using System.Diagnostics.CodeAnalysis;
88
using System.Diagnostics.ContractsLight;
9+
using System.Linq;
910
using System.Threading.Tasks;
10-
using BuildXL.Cache.Interfaces;
11-
using BuildXL.Cache.MemoizationStore.Distributed.Stores;
12-
using BuildXL.Utilities.Core;
13-
using BuildXL.Utilities.Configuration;
14-
using AbsolutePath = BuildXL.Cache.ContentStore.Interfaces.FileSystem.AbsolutePath;
1511
using BuildXL.Cache.ContentStore.Distributed;
1612
using BuildXL.Cache.ContentStore.Distributed.Blob;
1713
using BuildXL.Cache.ContentStore.Grpc;
18-
using BuildXL.Cache.ContentStore.Interfaces.Auth;
14+
using BuildXL.Cache.ContentStore.Interfaces.Logging;
1915
using BuildXL.Cache.ContentStore.Interfaces.Tracing;
20-
using BuildXL.Cache.ContentStore.Logging;
2116
using BuildXL.Cache.ContentStore.Tracing.Internal;
22-
using BuildXL.Cache.ContentStore.Interfaces.Logging;
17+
using BuildXL.Cache.Interfaces;
18+
using BuildXL.Utilities.Configuration;
19+
using BuildXL.Utilities.Core;
20+
using AbsolutePath = BuildXL.Cache.ContentStore.Interfaces.FileSystem.AbsolutePath;
2321

2422
namespace BuildXL.Cache.MemoizationStoreAdapter;
2523

@@ -39,18 +37,6 @@ public sealed class FactoryConfiguration : BlobCacheFactory.BlobCacheConfig
3937
[DefaultValue(typeof(CacheId))]
4038
public CacheId CacheId { get; set; }
4139

42-
/// <nodoc />
43-
[DefaultValue("EphemeralCacheConnectionString")]
44-
public string ManagementConnectionStringEnvironmentVariableName { get; set; }
45-
46-
/// <nodoc />
47-
[DefaultValue("default")]
48-
public string Universe { get; set; }
49-
50-
/// <nodoc />
51-
[DefaultValue("default")]
52-
public string Namespace { get; set; }
53-
5440
/// <summary>
5541
/// Path to the log file for the cache.
5642
/// </summary>
@@ -160,12 +146,9 @@ public async Task<Possible<ICache, Failure>> InitializeCacheAsync(FactoryConfigu
160146

161147
if (configuration.DatacenterWide)
162148
{
163-
var connectionString = Environment.GetEnvironmentVariable(configuration.ManagementConnectionStringEnvironmentVariableName);
164-
if (string.IsNullOrEmpty(connectionString))
165-
{
166-
throw new InvalidOperationException($"Can't find a connection string in environment variable '{configuration.ManagementConnectionStringEnvironmentVariableName}'.");
167-
}
168-
var credentials = new SecretBasedAzureStorageCredentials(connectionString);
149+
var accounts = BlobCacheFactory.LoadAzureCredentials(configuration);
150+
var sorted = ShardingScheme.SortAccounts(accounts.Keys.ToList());
151+
var credentials = accounts[sorted.First()];
169152

170153
factoryConfiguration = new ContentStore.Distributed.Ephemeral.EphemeralCacheFactory.DatacenterWideCacheConfiguration()
171154
{
@@ -188,24 +171,10 @@ public async Task<Possible<ICache, Failure>> InitializeCacheAsync(FactoryConfigu
188171
};
189172
}
190173

191-
var persistentCache = CreateBlobCache(configuration);
174+
var persistentCache = BlobCacheFactory.CreateCache(configuration);
192175
return await Cache.ContentStore.Distributed.Ephemeral.EphemeralCacheFactory.CreateAsync(context, factoryConfiguration, persistentCache);
193176
}
194177

195-
private static MemoizationStore.Interfaces.Caches.IFullCache CreateBlobCache(FactoryConfiguration configuration)
196-
{
197-
var credentials = BlobCacheFactory.GetAzureCredentialsFromBlobFactoryConfig(configuration);
198-
var accountName = BlobCacheStorageAccountName.Parse(credentials.GetAccountName());
199-
200-
var factoryConfiguration = new AzureBlobStorageCacheFactory.Configuration(
201-
ShardingScheme: new ShardingScheme(ShardingAlgorithm.SingleShard, new List<BlobCacheStorageAccountName> { accountName }),
202-
Universe: configuration.Universe,
203-
Namespace: configuration.Namespace,
204-
RetentionPolicyInDays: configuration.RetentionPolicyInDays);
205-
206-
return AzureBlobStorageCacheFactory.Create(factoryConfiguration, new StaticBlobCacheSecretsProvider(credentials));
207-
}
208-
209178
/// <inheritdoc />
210179
public IEnumerable<Failure> ValidateConfiguration(ICacheConfigData cacheData)
211180
{

0 commit comments

Comments
 (0)