Example #1
0
        public async Task <Package> ReflowAsync(string id, string version)
        {
            var package = _packageService.FindPackageByIdAndVersion(id, version);

            if (package == null)
            {
                return(null);
            }

            // Must suspend the retry execution strategy in order to use transactions.
            using (EntitiesConfiguration.SuspendRetriableExecutionStrategy())
            {
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    // 1) Download package binary to memory
                    using (var packageStream = await _packageFileService.DownloadPackageFileAsync(package))
                    {
                        using (var packageArchive = new PackageArchiveReader(packageStream, leaveStreamOpen: false))
                        {
                            // 2) Determine package metadata from binary
                            var packageStreamMetadata = new PackageStreamMetadata
                            {
                                HashAlgorithm = Constants.Sha512HashAlgorithmId,
                                Hash          = CryptographyService.GenerateHash(packageStream.AsSeekableStream()),
                                Size          = packageStream.Length,
                            };

                            var packageMetadata = PackageMetadata.FromNuspecReader(packageArchive.GetNuspecReader());

                            // 3) Clear referenced objects that will be reflowed
                            ClearSupportedFrameworks(package);
                            ClearAuthors(package);
                            ClearDependencies(package);

                            // 4) Reflow the package
                            var listed = package.Listed;

                            package = _packageService.EnrichPackageFromNuGetPackage(
                                package,
                                packageArchive,
                                packageMetadata,
                                packageStreamMetadata,
                                package.User);

                            package.LastEdited = DateTime.UtcNow;
                            package.Listed     = listed;

                            // 5) Save and profit
                            await _entitiesContext.SaveChangesAsync();
                        }
                    }

                    // Commit transaction
                    transaction.Commit();
                }
            }

            return(package);
        }
        public async Task HardDeletePackagesAsync(IEnumerable <Package> packages, User deletedBy, string reason, string signature, bool deleteEmptyPackageRegistration)
        {
            List <PackageRegistration> packageRegistrations;

            // Must suspend the retry execution strategy in order to use transactions.
            using (EntitiesConfiguration.SuspendRetriableExecutionStrategy())
            {
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    // Increase command timeout
                    _entitiesContext.SetCommandTimeout(seconds: 300);

                    // Keep package registrations
                    packageRegistrations = packages.GroupBy(p => p.PackageRegistration).Select(g => g.First().PackageRegistration).ToList();

                    // Backup the package binaries and remove from main storage
                    // We're doing this early in the process as we need the metadata to still exist in the DB.
                    await BackupPackageBinaries(packages);

                    // Remove the package and related entities from the database
                    foreach (var package in packages)
                    {
                        await ExecuteSqlCommandAsync(_entitiesContext.GetDatabase(),
                                                     "DELETE pa FROM PackageAuthors pa JOIN Packages p ON p.[Key] = pa.PackageKey WHERE p.[Key] = @key",
                                                     new SqlParameter("@key", package.Key));
                        await ExecuteSqlCommandAsync(_entitiesContext.GetDatabase(),
                                                     "DELETE pd FROM PackageDependencies pd JOIN Packages p ON p.[Key] = pd.PackageKey WHERE p.[Key] = @key",
                                                     new SqlParameter("@key", package.Key));
                        await ExecuteSqlCommandAsync(_entitiesContext.GetDatabase(),
                                                     "DELETE pf FROM PackageFrameworks pf JOIN Packages p ON p.[Key] = pf.Package_Key WHERE p.[Key] = @key",
                                                     new SqlParameter("@key", package.Key));

                        await _auditingService.SaveAuditRecordAsync(CreateAuditRecord(package, package.PackageRegistration, AuditedPackageAction.Delete, reason));

                        package.PackageRegistration.Packages.Remove(package);
                        _packageRepository.DeleteOnCommit(package);
                    }

                    // Commit changes to package repository
                    await _packageRepository.CommitChangesAsync();

                    // Remove package registrations that have no more packages?
                    if (deleteEmptyPackageRegistration)
                    {
                        await RemovePackageRegistrationsWithoutPackages(packageRegistrations);
                    }

                    // Commit transaction
                    transaction.Commit();
                }
            }

            // handle in separate transaction because of concurrency check with retry
            await UpdateIsLatestAsync(packageRegistrations);

            // Force refresh the index
            UpdateSearchIndex();
        }
        public async Task SoftDeletePackagesAsync(IEnumerable <Package> packages, User deletedBy, string reason, string signature)
        {
            List <PackageRegistration> packageRegistrations;

            // Must suspend the retry execution strategy in order to use transactions.
            using (EntitiesConfiguration.SuspendRetriableExecutionStrategy())
            {
                using (var transaction = _entitiesContext.GetDatabase().BeginTransaction())
                {
                    // Increase command timeout
                    _entitiesContext.SetCommandTimeout(seconds: 300);

                    // Keep package registrations
                    packageRegistrations = packages
                                           .GroupBy(p => p.PackageRegistration)
                                           .Select(g => g.First().PackageRegistration)
                                           .ToList();

                    // Backup the package binaries and remove from main storage
                    // We're doing this early in the process as we need the metadata to still exist in the DB.
                    await BackupPackageBinaries(packages);

                    // Store the soft delete in the database
                    var packageDelete = new PackageDelete
                    {
                        DeletedOn = DateTime.UtcNow,
                        DeletedBy = deletedBy,
                        Reason    = reason,
                        Signature = signature
                    };

                    foreach (var package in packages)
                    {
                        package.Listed  = false;
                        package.Deleted = true;
                        packageDelete.Packages.Add(package);

                        await _auditingService.SaveAuditRecordAsync(CreateAuditRecord(package, package.PackageRegistration, AuditedPackageAction.SoftDelete, reason));
                    }

                    _packageDeletesRepository.InsertOnCommit(packageDelete);

                    // Commit changes
                    await _packageRepository.CommitChangesAsync();

                    await _packageDeletesRepository.CommitChangesAsync();

                    transaction.Commit();
                }
            }

            // handle in separate transaction because of concurrency check with retry
            await UpdateIsLatestAsync(packageRegistrations);

            // Force refresh the index
            UpdateSearchIndex();
        }
Example #4
0
        public async Task UpdateIsLatestAsync(PackageRegistration packageRegistration)
        {
            // Must suspend the retry execution strategy in order to use transactions.
            using (EntitiesConfiguration.SuspendRetriableExecutionStrategy())
            {
                if (await TryUpdateIsLatestAsync(_entitiesContext, packageRegistration))
                {
                    return;
                }

                // Retry the update in case a concurrency conflict was detected on the first attempt.
                int retryCount = 1;
                do
                {
                    await Task.Delay(_randomGenerator.Value.Next(0, 1000));

                    _trace.Information(String.Format("Retrying {0} for package '{1}' ({2}/{3})",
                                                     nameof(UpdateIsLatestAsync), packageRegistration.Id, retryCount, UpdateIsLatestMaxRetries));

                    // Since EF contexts are short-lived and do not really support refresh, we will use a
                    // different context than the request on retry to avoid putting the request context in
                    // a bad state. More than likely the retry will detect that the concurrent update has
                    // already made the right updates and no changes will be necessary.
                    using (var detachedRetryContext = CreateNewEntitiesContext())
                    {
                        var detachedPackageRegistration = detachedRetryContext.PackageRegistrations.SingleOrDefault(
                            pr => pr.Id == packageRegistration.Id);

                        if (await TryUpdateIsLatestAsync(detachedRetryContext, detachedPackageRegistration))
                        {
                            return;
                        }
                    }
                    retryCount++;
                }while (retryCount <= UpdateIsLatestMaxRetries);
            }
        }