public async Task <IReadOnlyCollection <ObjectVersionRecord> > GetObjectVersions(long id, string initialVersionId) { var objectDescriptors = new List <ObjectDescriptor>(); async Task <(bool IsTruncated, int NextVersionIndex, string NextVersionIdMarker)> ListVersions(int nextVersionIndex, string nextVersionIdMarker) { await _distributedLockManager.EnsureLockNotExists(id); var response = await _s3Client.ListVersionsAsync( new ListVersionsRequest { BucketName = _bucketName, Prefix = id.AsS3ObjectKey(Tokens.ObjectPostfix), VersionIdMarker = nextVersionIdMarker }); var nonDeletedVersions = response.Versions.FindAll(x => !x.IsDeleteMarker); nextVersionIndex += nonDeletedVersions.Count; var initialVersionIdReached = false; var versionInfos = nonDeletedVersions .Aggregate( new List <(string VersionId, DateTime LastModified)>(), (list, next) => { initialVersionIdReached = initialVersionIdReached || !string.IsNullOrEmpty(initialVersionId) && initialVersionId.Equals(next.VersionId, StringComparison.OrdinalIgnoreCase); if (!initialVersionIdReached) { list.Add((next.VersionId, next.LastModified)); } return(list); }); var descriptors = new ObjectDescriptor[versionInfos.Count]; var partitioner = Partitioner.Create(versionInfos); var tasks = partitioner.GetOrderablePartitions(_degreeOfParallelism) .Select(async partition => { while (partition.MoveNext()) { var index = partition.Current.Key; var versionInfo = partition.Current.Value; descriptors[index] = await GetObjectDescriptor(id, versionInfo.VersionId); } }); await Task.WhenAll(tasks); objectDescriptors.AddRange(descriptors); return(!initialVersionIdReached && response.IsTruncated, nextVersionIndex, response.NextVersionIdMarker); } var result = await ListVersions(0, null); if (objectDescriptors.Count == 0) { throw new ObjectNotFoundException($"Object '{id}' not found."); } while (result.IsTruncated) { result = await ListVersions(result.NextVersionIndex, result.NextVersionIdMarker); } var maxVersionIndex = result.NextVersionIndex; var records = new ObjectVersionRecord[objectDescriptors.Count]; for (var index = 0; index < objectDescriptors.Count; ++index) { var descriptor = objectDescriptors[index]; records[index] = new ObjectVersionRecord( descriptor.Id, descriptor.VersionId, --maxVersionIndex, descriptor.LastModified, new AuthorInfo(descriptor.Metadata.Author, descriptor.Metadata.AuthorLogin, descriptor.Metadata.AuthorLogin), descriptor.Properties, descriptor.Elements.Select(x => new ObjectVersionRecord.ElementRecord(x.TemplateCode, x.Value)).ToList(), descriptor.Metadata.ModifiedElements); } return(records); }