Permalink
Browse files

Package download counts retrieved from CDN totals blob

  • Loading branch information...
maartenba committed Aug 25, 2015
1 parent 5b244d6 commit dca16252946f0728677b8a5f344b95721f79d5ac
@@ -2,12 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Data.Entity;
using System.Data.Entity.Infrastructure;

namespace NuGetGallery
{
public class EntitiesContext
: DbContext, IEntitiesContext
: EntityInterceptorDbContext, IEntitiesContext
{
static EntitiesContext()
{
@@ -16,22 +15,22 @@ static EntitiesContext()
}

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

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

public bool ReadOnly { get; private set; }
@@ -63,7 +62,7 @@ public override int SaveChanges()

public void SetCommandTimeout(int? seconds)
{
((IObjectContextAdapter)this).ObjectContext.CommandTimeout = seconds;
ObjectContext.CommandTimeout = seconds;
}

#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)
.HasRequired(cp => cp.PackageRegistration);
}
#pragma warning restore 618

}
}
@@ -0,0 +1,30 @@
// 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.Collections.Generic;

namespace NuGetGallery
{
public static class EntityInterception
{
private static readonly List<IEntityInterceptor> Interceptors = new List<IEntityInterceptor>();

public static void AddInterceptor(IEntityInterceptor interceptor)
{
Interceptors.Add(interceptor);
}

public static void InterceptObjectMaterialized(object entity)
{
if (entity == null)
{
return;
}

foreach (var interceptor in Interceptors)
{
interceptor.InterceptObjectMaterialized(entity);
}
}
}
}
@@ -0,0 +1,36 @@
// 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.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Objects;

namespace NuGetGallery
{
public class EntityInterceptorDbContext
: DbContext
{

public EntityInterceptorDbContext(string connectionString)
: base(connectionString)
{
ObjectContext.ObjectMaterialized += ObjectContextOnObjectMaterialized;
}

private void ObjectContextOnObjectMaterialized(object sender, ObjectMaterializedEventArgs objectMaterializedEventArgs)
{
EntityInterception.InterceptObjectMaterialized(objectMaterializedEventArgs.Entity);
}

protected ObjectContext ObjectContext
{
get { return ((IObjectContextAdapter)this).ObjectContext; }
}

protected override void Dispose(bool disposing)
{
ObjectContext.ObjectMaterialized -= ObjectContextOnObjectMaterialized;
base.Dispose(disposing);
}
}
}
@@ -0,0 +1,10 @@
// 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 NuGetGallery
{
public interface IEntityInterceptor
{
void InterceptObjectMaterialized(object entity);
}
}
@@ -106,10 +106,13 @@
<Compile Include="Entities\EmailMessage.cs" />
<Compile Include="Entities\EntitiesContext.cs" />
<Compile Include="Entities\EntityException.cs" />
<Compile Include="Entities\Interception\EntityInterception.cs" />
<Compile Include="Entities\Interception\EntityInterceptorDbContext.cs" />
<Compile Include="Entities\EntityRepository.cs" />
<Compile Include="Entities\GallerySetting.cs" />
<Compile Include="Entities\IEntitiesContext.cs" />
<Compile Include="Entities\IEntity.cs" />
<Compile Include="Entities\Interception\IEntityInterceptor.cs" />
<Compile Include="Entities\IEntityRepository.cs" />
<Compile Include="Entities\Package.cs" />
<Compile Include="Entities\PackageAuthor.cs" />
@@ -7,6 +7,7 @@
using System.IO;
using System.Security.Claims;
using System.Web.Helpers;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
@@ -196,6 +197,17 @@ private static void BackgroundJobsPostStart(IAppConfiguration configuration)
jobs.Add(CreateLogFlushJob());
}

if (configuration.StorageType == StorageType.AzureStorage)
{
var cloudDownloadCountService = DependencyResolver.Current.GetService<IDownloadCountService>() as CloudDownloadCountService;
if (cloudDownloadCountService != null)
{
// Perform initial refresh + schedule new refreshes every 15 minutes
HostingEnvironment.QueueBackgroundWorkItem(cancellationToken => cloudDownloadCountService.Refresh());
jobs.Add(new CloudDownloadCountServiceRefreshJob(TimeSpan.FromMinutes(15), cloudDownloadCountService));
}
}

if (jobs.AnySafe())
{
var jobCoordinator = new NuGetJobCoordinator();
@@ -339,6 +339,14 @@ private static void ConfigureForAzureStorage(ContainerBuilder builder, Configura
.As<IReportService>()
.SingleInstance();

// when running on Windows Azure, download counts come from the downloads.v1.json blob
var downloadCountService = new CloudDownloadCountService(configuration.Current.AzureStorageConnectionString, configuration.Current.AzureStorageReadAccessGeoRedundant);
builder.RegisterInstance(downloadCountService)
.AsSelf()
.As<IDownloadCountService>()
.SingleInstance();
EntityInterception.AddInterceptor(new DownloadCountEntityInterceptor(downloadCountService));

builder.RegisterType<JsonStatisticsService>()
.AsSelf()
.As<IStatisticsService>()
@@ -0,0 +1,25 @@
// 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;
using System.Threading.Tasks;
using WebBackgrounder;

namespace NuGetGallery
{
public class CloudDownloadCountServiceRefreshJob : Job
{
private readonly CloudDownloadCountService _downloadCountService;

public CloudDownloadCountServiceRefreshJob(TimeSpan interval, CloudDownloadCountService downloadCountService)
: base("", interval)
{
_downloadCountService = downloadCountService;
}

public override Task Execute()
{
return new Task(_downloadCountService.Refresh);
}
}
}
@@ -488,6 +488,7 @@
<Compile Include="Helpers\AppInsightsHealthIndicatorLogger.cs" />
<Compile Include="Helpers\Telemetry.cs" />
<Compile Include="Infrastructure\DependencyResolverServiceProviderAdapter.cs" />
<Compile Include="Infrastructure\Lucene\CloudDownloadCountServiceRefreshJob.cs" />
<Compile Include="Infrastructure\MessageQueue.cs" />
<Compile Include="Diagnostics\NullDiagnosticsSource.cs" />
<Compile Include="Diagnostics\TraceDiagnosticsSource.cs" />
@@ -1055,12 +1056,15 @@
<Content Include="Scripts\jquery-1.11.0.js" />
<Content Include="Scripts\jquery-1.11.0.min.js" />
<Compile Include="Services\AppActivatorException.cs" />
<Compile Include="Services\CloudDownloadCountService.cs" />
<Compile Include="Services\ConfirmOwnershipResult.cs" />
<Compile Include="Services\CuratedFeedService.cs" />
<Compile Include="Services\DownloadCountEntityInterceptor.cs" />
<Compile Include="Services\ICuratedFeedService.cs" />
<Compile Include="Services\CloudFileReference.cs" />
<Compile Include="Services\ContentService.cs" />
<Compile Include="Services\IContentService.cs" />
<Compile Include="Services\IDownloadCountService.cs" />
<Compile Include="Services\IFileReference.cs" />
<Compile Include="Services\IStatusService.cs" />
<Compile Include="Services\JsonStatisticsService.cs" />
Oops, something went wrong.

0 comments on commit dca1625

Please sign in to comment.