Skip to content

Commit dca1625

Browse files
committed
Package download counts retrieved from CDN totals blob
1 parent 5b244d6 commit dca1625

12 files changed

Lines changed: 404 additions & 13 deletions

src/NuGetGallery.Core/Entities/EntitiesContext.cs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.Data.Entity;
5-
using System.Data.Entity.Infrastructure;
65

76
namespace NuGetGallery
87
{
98
public class EntitiesContext
10-
: DbContext, IEntitiesContext
9+
: EntityInterceptorDbContext, IEntitiesContext
1110
{
1211
static EntitiesContext()
1312
{
@@ -16,22 +15,22 @@ static EntitiesContext()
1615
}
1716

1817
/// <summary>
19-
/// The NuGet Gallery code should usually use this constructor, in order to respect read only mode.
18+
/// This constructor is provided mainly for purposes of running migrations from Package Manager console,
19+
/// or any other scenario where a connection string will be set after the EntitiesContext is created
20+
/// (and read only mode is don't care).
2021
/// </summary>
21-
public EntitiesContext(string connectionString, bool readOnly)
22-
: base(connectionString)
22+
public EntitiesContext()
23+
: this("Gallery.SqlServer", false) // Use the connection string in a web.config (if one is found)
2324
{
24-
ReadOnly = readOnly;
2525
}
2626

2727
/// <summary>
28-
/// This constructor is provided mainly for purposes of running migrations from Package Manager console,
29-
/// or any other scenario where a connection string will be set after the EntitiesContext is created
30-
/// (and read only mode is don't care).
28+
/// The NuGet Gallery code should usually use this constructor, in order to respect read only mode.
3129
/// </summary>
32-
public EntitiesContext()
33-
: base("Gallery.SqlServer") // Use the connection string in a web.config (if one is found)
30+
public EntitiesContext(string connectionString, bool readOnly)
31+
: base(connectionString)
3432
{
33+
ReadOnly = readOnly;
3534
}
3635

3736
public bool ReadOnly { get; private set; }
@@ -63,7 +62,7 @@ public void DeleteOnCommit<T>(T entity) where T : class
6362

6463
public void SetCommandTimeout(int? seconds)
6564
{
66-
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = seconds;
65+
ObjectContext.CommandTimeout = seconds;
6766
}
6867

6968
#pragma warning disable 618 // TODO: remove Package.Authors completely once prodution services definitely no longer need it
@@ -214,6 +213,5 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder)
214213
.HasRequired(cp => cp.PackageRegistration);
215214
}
216215
#pragma warning restore 618
217-
218216
}
219217
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
6+
namespace NuGetGallery
7+
{
8+
public static class EntityInterception
9+
{
10+
private static readonly List<IEntityInterceptor> Interceptors = new List<IEntityInterceptor>();
11+
12+
public static void AddInterceptor(IEntityInterceptor interceptor)
13+
{
14+
Interceptors.Add(interceptor);
15+
}
16+
17+
public static void InterceptObjectMaterialized(object entity)
18+
{
19+
if (entity == null)
20+
{
21+
return;
22+
}
23+
24+
foreach (var interceptor in Interceptors)
25+
{
26+
interceptor.InterceptObjectMaterialized(entity);
27+
}
28+
}
29+
}
30+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Data.Entity;
5+
using System.Data.Entity.Infrastructure;
6+
using System.Data.Objects;
7+
8+
namespace NuGetGallery
9+
{
10+
public class EntityInterceptorDbContext
11+
: DbContext
12+
{
13+
14+
public EntityInterceptorDbContext(string connectionString)
15+
: base(connectionString)
16+
{
17+
ObjectContext.ObjectMaterialized += ObjectContextOnObjectMaterialized;
18+
}
19+
20+
private void ObjectContextOnObjectMaterialized(object sender, ObjectMaterializedEventArgs objectMaterializedEventArgs)
21+
{
22+
EntityInterception.InterceptObjectMaterialized(objectMaterializedEventArgs.Entity);
23+
}
24+
25+
protected ObjectContext ObjectContext
26+
{
27+
get { return ((IObjectContextAdapter)this).ObjectContext; }
28+
}
29+
30+
protected override void Dispose(bool disposing)
31+
{
32+
ObjectContext.ObjectMaterialized -= ObjectContextOnObjectMaterialized;
33+
base.Dispose(disposing);
34+
}
35+
}
36+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
namespace NuGetGallery
5+
{
6+
public interface IEntityInterceptor
7+
{
8+
void InterceptObjectMaterialized(object entity);
9+
}
10+
}

src/NuGetGallery.Core/NuGetGallery.Core.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,13 @@
106106
<Compile Include="Entities\EmailMessage.cs" />
107107
<Compile Include="Entities\EntitiesContext.cs" />
108108
<Compile Include="Entities\EntityException.cs" />
109+
<Compile Include="Entities\Interception\EntityInterception.cs" />
110+
<Compile Include="Entities\Interception\EntityInterceptorDbContext.cs" />
109111
<Compile Include="Entities\EntityRepository.cs" />
110112
<Compile Include="Entities\GallerySetting.cs" />
111113
<Compile Include="Entities\IEntitiesContext.cs" />
112114
<Compile Include="Entities\IEntity.cs" />
115+
<Compile Include="Entities\Interception\IEntityInterceptor.cs" />
113116
<Compile Include="Entities\IEntityRepository.cs" />
114117
<Compile Include="Entities\Package.cs" />
115118
<Compile Include="Entities\PackageAuthor.cs" />

src/NuGetGallery/App_Start/AppActivator.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.IO;
88
using System.Security.Claims;
99
using System.Web.Helpers;
10+
using System.Web.Hosting;
1011
using System.Web.Mvc;
1112
using System.Web.Optimization;
1213
using System.Web.Routing;
@@ -196,6 +197,17 @@ private static void BackgroundJobsPostStart(IAppConfiguration configuration)
196197
jobs.Add(CreateLogFlushJob());
197198
}
198199

200+
if (configuration.StorageType == StorageType.AzureStorage)
201+
{
202+
var cloudDownloadCountService = DependencyResolver.Current.GetService<IDownloadCountService>() as CloudDownloadCountService;
203+
if (cloudDownloadCountService != null)
204+
{
205+
// Perform initial refresh + schedule new refreshes every 15 minutes
206+
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => cloudDownloadCountService.Refresh());
207+
jobs.Add(new CloudDownloadCountServiceRefreshJob(TimeSpan.FromMinutes(15), cloudDownloadCountService));
208+
}
209+
}
210+
199211
if (jobs.AnySafe())
200212
{
201213
var jobCoordinator = new NuGetJobCoordinator();

src/NuGetGallery/App_Start/DefaultDependenciesModule.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,14 @@ private static void ConfigureForAzureStorage(ContainerBuilder builder, Configura
339339
.As<IReportService>()
340340
.SingleInstance();
341341

342+
// when running on Windows Azure, download counts come from the downloads.v1.json blob
343+
var downloadCountService = new CloudDownloadCountService(configuration.Current.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant);
344+
builder.RegisterInstance(downloadCountService)
345+
.AsSelf()
346+
.As<IDownloadCountService>()
347+
.SingleInstance();
348+
EntityInterception.AddInterceptor(new DownloadCountEntityInterceptor(downloadCountService));
349+
342350
builder.RegisterType<JsonStatisticsService>()
343351
.AsSelf()
344352
.As<IStatisticsService>()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using WebBackgrounder;
7+
8+
namespace NuGetGallery
9+
{
10+
public class CloudDownloadCountServiceRefreshJob : Job
11+
{
12+
private readonly CloudDownloadCountService _downloadCountService;
13+
14+
public CloudDownloadCountServiceRefreshJob(TimeSpan interval, CloudDownloadCountService downloadCountService)
15+
: base("", interval)
16+
{
17+
_downloadCountService = downloadCountService;
18+
}
19+
20+
public override Task Execute()
21+
{
22+
return new Task(_downloadCountService.Refresh);
23+
}
24+
}
25+
}

src/NuGetGallery/NuGetGallery.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,7 @@
488488
<Compile Include="Helpers\AppInsightsHealthIndicatorLogger.cs" />
489489
<Compile Include="Helpers\Telemetry.cs" />
490490
<Compile Include="Infrastructure\DependencyResolverServiceProviderAdapter.cs" />
491+
<Compile Include="Infrastructure\Lucene\CloudDownloadCountServiceRefreshJob.cs" />
491492
<Compile Include="Infrastructure\MessageQueue.cs" />
492493
<Compile Include="Diagnostics\NullDiagnosticsSource.cs" />
493494
<Compile Include="Diagnostics\TraceDiagnosticsSource.cs" />
@@ -1055,12 +1056,15 @@
10551056
<Content Include="Scripts\jquery-1.11.0.js" />
10561057
<Content Include="Scripts\jquery-1.11.0.min.js" />
10571058
<Compile Include="Services\AppActivatorException.cs" />
1059+
<Compile Include="Services\CloudDownloadCountService.cs" />
10581060
<Compile Include="Services\ConfirmOwnershipResult.cs" />
10591061
<Compile Include="Services\CuratedFeedService.cs" />
1062+
<Compile Include="Services\DownloadCountEntityInterceptor.cs" />
10601063
<Compile Include="Services\ICuratedFeedService.cs" />
10611064
<Compile Include="Services\CloudFileReference.cs" />
10621065
<Compile Include="Services\ContentService.cs" />
10631066
<Compile Include="Services\IContentService.cs" />
1067+
<Compile Include="Services\IDownloadCountService.cs" />
10641068
<Compile Include="Services\IFileReference.cs" />
10651069
<Compile Include="Services\IStatusService.cs" />
10661070
<Compile Include="Services\JsonStatisticsService.cs" />

0 commit comments

Comments
 (0)