Skip to content

Commit 54b7ae6

Browse files
authored
Preparing ErrorLog for secret refresh. (#8485)
1 parent 1e6caa9 commit 54b7ae6

3 files changed

Lines changed: 46 additions & 25 deletions

File tree

src/NuGetGallery.Core/Infrastructure/AzureEntityList.cs

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,25 @@ namespace NuGetGallery.Infrastructure
2222
private const string IndexPartitionKey = "INDEX";
2323
private const string IndexRowKey = "0";
2424

25-
private CloudTable _tableRef;
25+
private readonly string _tableName;
26+
private readonly bool _readAccessGeoRedundant;
27+
private readonly Func<string> _connectionStringFactory;
2628

27-
public AzureEntityList(string connStr, string tableName, bool readAccessGeoRedundant)
29+
public AzureEntityList(Func<string> connectionStringFactory, string tableName, bool readAccessGeoRedundant)
2830
{
29-
var tableClient = CloudStorageAccount.Parse(connStr).CreateCloudTableClient();
30-
if (readAccessGeoRedundant)
31-
{
32-
tableClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
33-
}
34-
_tableRef = tableClient.GetTableReference(tableName);
31+
_tableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
32+
_readAccessGeoRedundant = readAccessGeoRedundant;
33+
_connectionStringFactory = connectionStringFactory ?? throw new ArgumentNullException(nameof(connectionStringFactory));
3534

35+
var tableRef = GetTableReference();
3636
// Create the actual Azure Table, if it doesn't yet exist.
37-
bool newTable = _tableRef.CreateIfNotExists();
37+
bool newTable = tableRef.CreateIfNotExists();
3838

3939
// Create the Index if it doesn't yet exist.
4040
bool needsIndex = newTable;
4141
if (!newTable)
4242
{
43-
var indexResult = _tableRef.Execute(
43+
var indexResult = tableRef.Execute(
4444
TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));
4545

4646
needsIndex = (indexResult.HttpStatusCode == 404);
@@ -49,7 +49,7 @@ public AzureEntityList(string connStr, string tableName, bool readAccessGeoRedun
4949
if (needsIndex)
5050
{
5151
// Create the index
52-
var result = _tableRef.Execute(
52+
var result = tableRef.Execute(
5353
TableOperation.Insert(new Index
5454
{
5555
Count = 0,
@@ -102,7 +102,8 @@ public T this[long index]
102102
string partitionKey = FormatPartitionKey(page);
103103
string rowKey = FormatRowKey(row);
104104

105-
var response = _tableRef.Execute(TableOperation.Retrieve<T>(partitionKey, rowKey));
105+
var tableRef = GetTableReference();
106+
var response = tableRef.Execute(TableOperation.Retrieve<T>(partitionKey, rowKey));
106107
if (response.HttpStatusCode == 404)
107108
{
108109
throw new ArgumentOutOfRangeException(nameof(index), index, CoreStrings.Http404NotFound);
@@ -129,8 +130,10 @@ public T this[long index]
129130
value.PartitionKey = FormatPartitionKey(page);
130131
value.RowKey = FormatRowKey(row);
131132

133+
var tableRef = GetTableReference();
134+
132135
// Just do an unconditional update - if you wanted any *real* benefit of atomic update then you would need a more complex method signature that calls you back when optimistic updates fail ETAG checks!
133-
_tableRef.Execute(TableOperation.Replace(value));
136+
tableRef.Execute(TableOperation.Replace(value));
134137
}
135138
}
136139

@@ -161,13 +164,14 @@ public long Add(T entity)
161164
/// </summary>
162165
public IEnumerator<T> GetEnumerator()
163166
{
167+
var tableRef = GetTableReference();
164168
for (long page = 0;; page++)
165169
{
166170
string partitionKey = FormatPartitionKey(page);
167171
var chunkQuery = new TableQuery<T>().Where(
168172
TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
169173

170-
var chunk = _tableRef.ExecuteQuery(chunkQuery).ToArray();
174+
var chunk = tableRef.ExecuteQuery(chunkQuery).ToArray();
171175

172176
foreach (var item in chunk)
173177
{
@@ -191,6 +195,7 @@ public IEnumerable<T> GetRange(long pos, int n)
191195
int done = 0;
192196
long page = pos / 1000;
193197
long offset = pos % 1000;
198+
var tableRef = GetTableReference();
194199
while (done < n)
195200
{
196201
string partitionKey = FormatPartitionKey(page);
@@ -201,7 +206,7 @@ public IEnumerable<T> GetRange(long pos, int n)
201206
TableOperators.And,
202207
TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, rowKey)));
203208

204-
var chunk = _tableRef.ExecuteQuery(chunkQuery).ToArray();
209+
var chunk = tableRef.ExecuteQuery(chunkQuery).ToArray();
205210
if (chunk.Length == 0)
206211
{
207212
break; // Reached the end of the list
@@ -235,9 +240,10 @@ private long AtomicIncrementCount()
235240
// 2) use ETAG to do a conditional +1 update
236241
// 3) retry if that optimistic concurrency attempt failed
237242
long pos = -1; // To avoid compiler warnings, grr - should never be returned
243+
var tableRef = GetTableReference();
238244
DoReplaceWithRetry(() =>
239245
{
240-
var result1 = _tableRef.Execute(
246+
var result1 = tableRef.Execute(
241247
TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));
242248

243249
ThrowIfErrorStatus(result1);
@@ -260,6 +266,7 @@ private long AtomicIncrementCount()
260266
private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 : ITableEntity
261267
{
262268
TableResult storeResult;
269+
var tableRef = GetTableReference();
263270
do
264271
{
265272
var entity = valueGenerator();
@@ -268,7 +275,7 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
268275
// - the dummy MERGES with existing data instead of overwriting it, so no data loss.
269276
// 2) Use its ETAG to conditionally replace the item
270277
// 3) return true if success, false to allow retry on failure
271-
var dummyResult = _tableRef.Execute(
278+
var dummyResult = tableRef.Execute(
272279
TableOperation.InsertOrMerge(new HazardEntry
273280
{
274281
PartitionKey = entity.PartitionKey,
@@ -281,7 +288,7 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
281288
}
282289

283290
entity.ETag = dummyResult.Etag;
284-
storeResult = _tableRef.Execute(TableOperation.Replace(entity));
291+
storeResult = tableRef.Execute(TableOperation.Replace(entity));
285292
}
286293
while (storeResult.HttpStatusCode == 412);
287294
ThrowIfErrorStatus(storeResult);
@@ -290,17 +297,19 @@ private void InsertIfNotExistsWithRetry<T2>(Func<T2> valueGenerator) where T2 :
290297
private void DoReplaceWithRetry<T2>(Func<T2> valueGenerator) where T2 : ITableEntity
291298
{
292299
TableResult storeResult;
300+
var tableRef = GetTableReference();
293301
do
294302
{
295-
storeResult = _tableRef.Execute(TableOperation.Replace(valueGenerator.Invoke()));
303+
storeResult = tableRef.Execute(TableOperation.Replace(valueGenerator.Invoke()));
296304
}
297305
while (storeResult.HttpStatusCode == 412);
298306
ThrowIfErrorStatus(storeResult);
299307
}
300308

301309
private Index ReadIndex()
302310
{
303-
var response = _tableRef.Execute(TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));
311+
var tableRef = GetTableReference();
312+
var response = tableRef.Execute(TableOperation.Retrieve<Index>(IndexPartitionKey, IndexRowKey));
304313
return (Index)response.Result;
305314
}
306315

@@ -333,6 +342,16 @@ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
333342
return GetEnumerator();
334343
}
335344

345+
private CloudTable GetTableReference()
346+
{
347+
var tableClient = CloudStorageAccount.Parse(_connectionStringFactory()).CreateCloudTableClient();
348+
if (_readAccessGeoRedundant)
349+
{
350+
tableClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
351+
}
352+
return tableClient.GetTableReference(_tableName);
353+
}
354+
336355
class HazardEntry : ITableEntity
337356
{
338357
private const string PlaceHolderPropertyName = "Place_Held";

src/NuGetGallery.Core/Infrastructure/TableErrorLog.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,11 @@ public class TableErrorLog : ErrorLog
141141
{
142142
public const string TableName = "ElmahErrors";
143143

144-
private readonly string _connectionString;
145144
private readonly AzureEntityList<ErrorEntity> _entityList;
146145

147-
public TableErrorLog(string connectionString, bool readAccessGeoRedundant)
146+
public TableErrorLog(Func<string> connectionStringFactory, bool readAccessGeoRedundant)
148147
{
149-
_connectionString = connectionString;
150-
_entityList = new AzureEntityList<ErrorEntity>(connectionString, TableName, readAccessGeoRedundant);
148+
_entityList = new AzureEntityList<ErrorEntity>(connectionStringFactory, TableName, readAccessGeoRedundant);
151149
}
152150

153151
public override ErrorLogEntry GetError(string id)

src/NuGetGallery/App_Start/DefaultDependenciesModule.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,11 @@ private static void ConfigureForAzureStorage(ContainerBuilder builder, IGalleryC
14361436

14371437
RegisterStatisticsServices(builder, configuration, telemetryService);
14381438

1439-
builder.RegisterInstance(new TableErrorLog(configuration.Current.AzureStorage_Errors_ConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant))
1439+
builder.Register(c =>
1440+
{
1441+
var configurationFactory = c.Resolve<Func<IAppConfiguration>>();
1442+
return new TableErrorLog(() => configurationFactory().AzureStorage_Errors_ConnectionString, configurationFactory().AzureStorageReadAccessGeoRedundant);
1443+
})
14401444
.As<ErrorLog>()
14411445
.SingleInstance();
14421446

0 commit comments

Comments
 (0)