Skip to content
This repository has been archived by the owner on Jul 30, 2024. It is now read-only.
/ NuGet.Jobs Public archive

Update PackageLagMonitor for AzureSearch #798

Merged
merged 6 commits into from
Aug 21, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/PackageLagMonitor/AzureSearchDiagnosticResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copyright header

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added


namespace NuGet.Jobs.Monitoring.PackageLag
{
public class AzureSearchDiagnosticResponse
{
public bool Success { get; set; }

public TimeSpan Duration { get; set; }

public Server Server { get; set; }

public IndexInformation SearchIndex { get; set; }

public IndexInformation HijackIndex { get; set; }

public AuxiliaryFileInformations AuxiliaryFiles { get; set; }
}

public class Server
{
public string MachineName { get; set; }

public long ProcessId { get; set; }

public DateTimeOffset ProcessStartTime { get; set; }

public TimeSpan ProcessDuration { get; set; }

public string DeploymentLabel { get; set; }

public string AssemblyCommitId { get; set; }

public string AssemblyInformationVersion { get; set; }

public DateTimeOffset AssemblyBuildDateUtc { get; set; }

public string InstanceId { get; set; }
}

public class IndexInformation
{
public string Name { get; set; }

public long DocumentCount { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: only add the properties you use... the shape has been kind of fluid...

The LastCommitTimestamp can't change though since it's already used by a runner


public TimeSpan DocumentCountDuration { get; set; }

public TimeSpan WarmQueryDuration { get; set; }

public DateTimeOffset LastCommitTimestamp { get; set; }

public TimeSpan LastCommitTimestampDuration { get; set; }
}

public class AuxiliaryFileInformations
{
public AuxiliaryFileInformation Downloads { get; set; }

public AuxiliaryFileInformation VerifiedPackages { get; set; }
}

public class AuxiliaryFileInformation
{
public DateTimeOffset LastModified { get; set; }

public DateTimeOffset Loaded { get; set; }

public TimeSpan LoadDuration { get; set; }

public long FileSize { get; set; }

public string ETag { get; set; }
}
}
4 changes: 3 additions & 1 deletion src/PackageLagMonitor/Instance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@ namespace NuGet.Jobs.Monitoring.PackageLag
{
public class Instance
{
public Instance(string slot, int index, string diagUrl, string baseQueryUrl, string region)
public Instance(string slot, int index, string diagUrl, string baseQueryUrl, string region, ServiceType serviceType)
{
Slot = slot ?? throw new ArgumentNullException(nameof(slot));
Index = index;
DiagUrl = diagUrl ?? throw new ArgumentNullException(nameof(diagUrl));
BaseQueryUrl = baseQueryUrl ?? throw new ArgumentNullException(nameof(baseQueryUrl));
Region = region ?? throw new ArgumentNullException(nameof(region));
ServiceType = serviceType;
}

public string Slot { get; }
public int Index { get; }
public string DiagUrl { get; }
public string BaseQueryUrl { get; }
public string Region { get; }
public ServiceType ServiceType { get; }
}
}
3 changes: 3 additions & 0 deletions src/PackageLagMonitor/Monitoring.PackageLag.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="AzureManagementAPIWrapperConfiguration.cs" />
<Compile Include="AzureSearchDiagnosticResponse.cs" />
<Compile Include="HttpWrappers\HttpClientWrapper.cs" />
<Compile Include="HttpResponseException.cs" />
<Compile Include="HttpWrappers\IHttpClientWrapper.cs" />
Expand All @@ -67,8 +68,10 @@
<Compile Include="SearchResultResponse.cs" />
<Compile Include="SearchServiceClient.cs" />
<Compile Include="SearchServiceConfiguration.cs" />
<Compile Include="ServiceType.cs" />
<Compile Include="Telemetry\IPackageLagTelemetryService.cs" />
<Compile Include="Telemetry\PackageLagTelemetryService.cs" />
<Compile Include="UnknownServiceTypeException.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config">
Expand Down
13 changes: 6 additions & 7 deletions src/PackageLagMonitor/PackageLagCatalogLeafProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using NuGet.Jobs.Monitoring.PackageLag.Telemetry;
using NuGet.Protocol.Catalog;

Expand Down Expand Up @@ -132,6 +130,7 @@ public Task<bool> ProcessPackageDetailsAsync(PackageDetailsCatalogLeaf leaf)
CancellationToken token)
{
await Task.Yield();
var serviceTypeString = Enum.GetName(typeof(ServiceType), instance.ServiceType);

try
{
Expand Down Expand Up @@ -171,7 +170,7 @@ public Task<bool> ProcessPackageDetailsAsync(PackageDetailsCatalogLeaf leaf)
if (shouldRetry)
{
++retryCount;
_logger.LogInformation("Waiting for {RetryTime} seconds before retrying {PackageId} {PackageVersion} against {SearchBaseUrl}", WaitBetweenPolls.TotalSeconds, packageId, packageVersion, instance.BaseQueryUrl);
_logger.LogInformation("{ServiceType}: Waiting for {RetryTime} seconds before retrying {PackageId} {PackageVersion} against {SearchBaseUrl}", serviceTypeString, WaitBetweenPolls.TotalSeconds, packageId, packageVersion, instance.BaseQueryUrl);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass the enum as-is. The ToString() will be called and result in this came thing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. I'll do that instead.

await Task.Delay(WaitBetweenPolls);
}
} while (shouldRetry && retryCount < RetryLimit);
Expand All @@ -187,8 +186,8 @@ public Task<bool> ProcessPackageDetailsAsync(PackageDetailsCatalogLeaf leaf)
var timeStamp = (isListOperation ? lastEdited : created);

// We log both of these values here as they will differ if a package went through validation pipline.
_logger.LogInformation("Lag {Timestamp}:{PackageId} {PackageVersion} SearchInstance:{Region}{Instance} Created: {CreatedLag} V3: {V3Lag}", timeStamp, packageId, packageVersion, instance.Region, instance.Index, createdDelay, v3Delay);
_logger.LogInformation("LastReload:{LastReloadTimestamp} LastEdited:{LastEditedTimestamp} Created:{CreatedTimestamp} ", lastReloadTime, lastEdited, created);
_logger.LogInformation("{ServiceType}: Lag {Timestamp}:{PackageId} {PackageVersion} SearchInstance:{Region}{Instance} Created: {CreatedLag} V3: {V3Lag}", serviceTypeString, timeStamp, packageId, packageVersion, instance.Region, instance.Index, createdDelay, v3Delay);
_logger.LogInformation("{ServiceType}: LastReload:{LastReloadTimestamp} LastEdited:{LastEditedTimestamp} Created:{CreatedTimestamp} ", serviceTypeString, lastReloadTime, lastEdited, created);
if (!isListOperation)
{
_telemetryService.TrackPackageCreationLag(timeStamp, instance, packageId, packageVersion, createdDelay);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't you be piping the instance.ServiceType to here and the other _telemetryService call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the changes to TelemetryService already do this since we have access to the ServiceType on the instance.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see now!

Expand All @@ -204,12 +203,12 @@ public Task<bool> ProcessPackageDetailsAsync(PackageDetailsCatalogLeaf leaf)
}
else
{
_logger.LogInformation("Lag check for {PackageId} {PackageVersion} was abandoned. Retry limit reached.", packageId, packageVersion);
_logger.LogInformation("{ServiceType}: Lag check for {PackageId} {PackageVersion} was abandoned. Retry limit reached.", serviceTypeString, packageId, packageVersion);
}
}
catch (Exception e)
{
_logger.LogError("Failed to compute lag for {PackageId} {PackageVersion}. {Exception}", packageId, packageVersion, e);
_logger.LogError("{ServiceType}: Failed to compute lag for {PackageId} {PackageVersion}. {Exception}", serviceTypeString, packageId, packageVersion, e);
}

return null;
Expand Down
11 changes: 10 additions & 1 deletion src/PackageLagMonitor/RegionInformation.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.


namespace NuGet.Jobs.Monitoring.PackageLag
{
public class RegionInformation
Expand All @@ -11,5 +10,15 @@ public class RegionInformation
public string ServiceName { get; set; }

public string Region { get; set; }

/// <summary>
/// Base url to use for queries. Used for AzureSearch case.
/// </summary>
public string BaseUrl { get; set; }

/// <summary>
/// One of LuceneSearch or AzureSearch depending on what type of search service this information defines.
/// </summary>
public ServiceType ServiceType { get; set; }
}
}
94 changes: 74 additions & 20 deletions src/PackageLagMonitor/SearchServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,17 @@ public async Task<SearchDiagnosticResponse> GetSearchDiagnosticResponseAsync(

var diagContent = diagResponse.Content;
var searchDiagResultRaw = await diagContent.ReadAsStringAsync();
var response = JsonConvert.DeserializeObject<SearchDiagnosticResponse>(searchDiagResultRaw);
SearchDiagnosticResponse response = null;
switch (instance.ServiceType)
{
case ServiceType.LuceneSearch:
response = JsonConvert.DeserializeObject<SearchDiagnosticResponse>(searchDiagResultRaw);
break;
case ServiceType.AzureSearch:
var tempResponse = JsonConvert.DeserializeObject<AzureSearchDiagnosticResponse>(searchDiagResultRaw);
response = ConvertAzureSearchResponse(tempResponse);
break;
}

return response;
}
Expand All @@ -107,18 +117,26 @@ public async Task<IReadOnlyList<Instance>> GetSearchEndpointsAsync(
RegionInformation regionInformation,
CancellationToken token)
{
var result = await _azureManagementApiWrapper.GetCloudServicePropertiesAsync(
_configuration.Value.Subscription,
regionInformation.ResourceGroup,
regionInformation.ServiceName,
ProductionSlot,
token);
switch (regionInformation.ServiceType)
{
case ServiceType.LuceneSearch:
var result = await _azureManagementApiWrapper.GetCloudServicePropertiesAsync(
_configuration.Value.Subscription,
regionInformation.ResourceGroup,
regionInformation.ServiceName,
ProductionSlot,
token);

var cloudService = AzureHelper.ParseCloudServiceProperties(result);
var cloudService = AzureHelper.ParseCloudServiceProperties(result);

var instances = GetInstances(cloudService.Uri, cloudService.InstanceCount, regionInformation);
var instances = GetInstances(endpointUri: cloudService.Uri, instanceCount: cloudService.InstanceCount, regionInformation: regionInformation, serviceType: ServiceType.LuceneSearch);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these named params are a bit redundant. the parameters themselves are very descriptive in this case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


return instances;
return instances;
case ServiceType.AzureSearch:
return GetInstances(endpointUri: new Uri(regionInformation.BaseUrl), instanceCount: 1, regionInformation: regionInformation, serviceType: ServiceType.AzureSearch);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the code would be a little simpler if you made it construct an instance here directly instead of calling GetInstances. GetInstances does a lot of work to create multiple instances that's not necessary for the Azure Search case.

I would recommend:

  1. Moving the code inside the Enumerable.Range(...).Select(...) that generates an instance into its own function.
  2. Replacing this call with a call to that function.
  3. Renaming GetInstances to GetLuceneInstances.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean here by simpler.
The switch for specifying port would still need to exist (as the new function would still need to be able to specify this), and the case for AzureSearch would have to manually wrap the return in a list (note the return type of the method).
I think it makes more sense to treat azure search instance as a "LuceneSearch" type with 1 instance (and admittedly no port) rather than trying to completely separate them.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, it's a little confusing that GetInstances will always enumerate an enumerable with a single member in the AzureSearch case. The only thing that's really shared logic in that function is the construction of an instance (which, admittedly, has to check service type) so I was thinking you could move that out. Not a big deal though.

default:
throw new UnknownServiceTypeException();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can throw an existing exception like NotImplementated with a message containing the service type to avoid the custom exception

}
}

public async Task<SearchResultResponse> GetSearchResultAsync(Instance instance, string query, CancellationToken token)
Expand Down Expand Up @@ -160,15 +178,26 @@ public async Task<SearchResultResponse> GetSearchResultAsync(Instance instance,
throw new NotImplementedException();
}

private List<Instance> GetInstances(Uri endpointUri, int instanceCount, RegionInformation regionInformation)
private List<Instance> GetInstances(Uri endpointUri, int instanceCount, RegionInformation regionInformation, ServiceType serviceType)
{
var instancePortMinimum = _configuration.Value.InstancePortMinimum;

_logger.LogInformation(
"Testing {InstanceCount} instances, starting at port {InstancePortMinimum} for region {Region}.",
instanceCount,
instancePortMinimum,
regionInformation.Region);
switch (serviceType)
{
case ServiceType.LuceneSearch:
_logger.LogInformation(
"{ServiceType}: Testing {InstanceCount} instances, starting at port {InstancePortMinimum} for region {Region}.",
Enum.GetName(typeof(ServiceType), ServiceType.LuceneSearch),
instanceCount,
instancePortMinimum,
regionInformation.Region);
break;
case ServiceType.AzureSearch:
_logger.LogInformation(
"{ServiceType}: Testing for region {Region}.",
Enum.GetName(typeof(ServiceType), ServiceType.AzureSearch),
regionInformation.Region);
break;
}

return Enumerable
.Range(0, instanceCount)
Expand All @@ -177,23 +206,48 @@ private List<Instance> GetInstances(Uri endpointUri, int instanceCount, RegionIn
var diagUriBuilder = new UriBuilder(endpointUri);

diagUriBuilder.Scheme = "https";
diagUriBuilder.Port = instancePortMinimum + i;
if (serviceType == ServiceType.LuceneSearch)
{
diagUriBuilder.Port = instancePortMinimum + i;
}
diagUriBuilder.Path = "search/diag";

var queryBaseUriBuilder = new UriBuilder(endpointUri);

queryBaseUriBuilder.Scheme = "https";
queryBaseUriBuilder.Port = instancePortMinimum + i;
if (serviceType == ServiceType.LuceneSearch)
{
queryBaseUriBuilder.Port = instancePortMinimum + i;
}
queryBaseUriBuilder.Path = "search/query";

return new Instance(
ProductionSlot,
i,
diagUriBuilder.Uri.ToString(),
queryBaseUriBuilder.Uri.ToString(),
regionInformation.Region);
regionInformation.Region,
serviceType);
})
.ToList();
}

private SearchDiagnosticResponse ConvertAzureSearchResponse(AzureSearchDiagnosticResponse azureSearchDiagnosticResponse)
{
var result = new SearchDiagnosticResponse()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: you can drop the () if you use object initializer syntax

Suggested change
var result = new SearchDiagnosticResponse()
var result = new SearchDiagnosticResponse

Same comment for line 240

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
NumDocs = azureSearchDiagnosticResponse.SearchIndex.DocumentCount,
IndexName = azureSearchDiagnosticResponse.SearchIndex.Name,
LastIndexReloadTime = azureSearchDiagnosticResponse.SearchIndex.LastCommitTimestamp,
LastIndexReloadDurationInMilliseconds = (long)azureSearchDiagnosticResponse.SearchIndex.LastCommitTimestampDuration.TotalMilliseconds,
LastReopen = azureSearchDiagnosticResponse.SearchIndex.LastCommitTimestamp,
CommitUserData = new CommitUserData()
{
CommitTimeStamp = azureSearchDiagnosticResponse.SearchIndex.LastCommitTimestamp.ToString()
}
};

return result;
}
}
}
11 changes: 11 additions & 0 deletions src/PackageLagMonitor/ServiceType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace NuGet.Jobs.Monitoring.PackageLag
{
public enum ServiceType
{
LuceneSearch,
AzureSearch
}
}
7 changes: 5 additions & 2 deletions src/PackageLagMonitor/Telemetry/PackageLagTelemetryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class PackageLagTelemetryService : IPackageLagTelemetryService
private const string Region = "Region";
private const string Subscription = "Subscription";
private const string InstanceIndex = "InstanceIndex";
private const string ServiceType = "ServiceType";

private const string CreatedLagName = "PackageCreationLagInSeconds";
private const string V3LagName = "V3LagInSeconds";
Expand All @@ -36,7 +37,8 @@ public void TrackPackageCreationLag(DateTimeOffset eventTime, Instance instance,
{ PackageVersion, packageVersion },
{ Region, instance.Region },
{ Subscription, _subscription },
{ InstanceIndex, instance.Index.ToString() }
{ InstanceIndex, instance.Index.ToString() },
{ ServiceType, Enum.GetName(typeof(ServiceType), instance.ServiceType) }
});
}

Expand All @@ -48,7 +50,8 @@ public void TrackV3Lag(DateTimeOffset eventTime, Instance instance, string packa
{ PackageVersion, packageVersion },
{ Region, instance.Region },
{ Subscription, _subscription },
{ InstanceIndex, instance.Index.ToString() }
{ InstanceIndex, instance.Index.ToString() },
{ ServiceType, Enum.GetName(typeof(ServiceType), instance.ServiceType) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just do instance.ServiceType.ToString()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updasted

});
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/PackageLagMonitor/UnknownServiceTypeException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace NuGet.Jobs.Monitoring.PackageLag
{
public class UnknownServiceTypeException : Exception
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

{
}
}
Loading