CreateSnapshotAsync(Guid ownerId, Guid scoreId, Guid snapshotId, string snapshotName)
        {
            var dynamoDbScore = await GetAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId);

            var hashSet = await GetAnnotationsAsync(_dynamoDbClient, ScoreDataTableName, ownerId, scoreId);

            var descriptionHash = dynamoDbScore.Data.GetDescriptionHash();

            var(success, description) =
                await TryGetDescriptionAsync(_dynamoDbClient, ScoreDataTableName, ownerId, scoreId, descriptionHash);

            var itemRelationIds = dynamoDbScore.Data.GetPages().Select(x => x.ItemId).ToArray();

            if (success)
            {
                hashSet[descriptionHash] = description;
            }

            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            var maxSnapshotCount = _quota.SnapshotCountMax;

            await UpdateAsync(
                _dynamoDbClient, ScoreTableName, ownerId, scoreId, snapshotId
                , snapshotName, now, maxSnapshotCount);

            await PutItemRelations(_dynamoDbClient, ScoreItemRelationTableName, ownerId, snapshotId, itemRelationIds);


            return(dynamoDbScore, hashSet);
示例#2
0
            static async Task <(string itemIdText, Guid itemId, long size)[]> GetItemAndSizeListAsync(IAmazonDynamoDB client, string tableName, Guid ownerId, Guid scoreId)
            {
                var partitionKey = ScoreItemDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var request = new QueryRequest()
                {
                    TableName = tableName,
                    ExpressionAttributeNames = new Dictionary <string, string>()
                    {
                        ["#owner"] = ScoreItemDatabasePropertyNames.OwnerId,
                        ["#item"]  = ScoreItemDatabasePropertyNames.ItemId,
                    },
                    ExpressionAttributeValues = new Dictionary <string, AttributeValue>()
                    {
                        [":owner"] = new AttributeValue(partitionKey),
                        [":score"] = new AttributeValue(score),
                    },
                    KeyConditionExpression = "#owner = :owner and begins_with(#item, :score)",
                };

                try
                {
                    var response = await client.QueryAsync(request);

                    return(response.Items
                           .Where(x => x[ScoreItemDatabasePropertyNames.ItemId].S != ScoreItemDatabaseConstant.ItemIdSummary)
                           .Select(GetItemAndSizeAsync).ToArray());
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
示例#3
0
        public async Task CreateAsync(ScoreItemDatabaseItemDataBase itemData)
        {
            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            var maxSize = _quota.OwnerItemMaxSize;

            await PutDataAsync(_dynamoDbClient, ScoreTableName, itemData, maxSize, now);
示例#4
0
                static (string itemIdText, Guid itemId, long size) GetItemAndSizeAsync(Dictionary <string, AttributeValue> items)
                {
                    var sizeValue = items[ScoreItemDatabasePropertyNames.Size];
                    var size      = long.Parse(sizeValue.N, CultureInfo.InvariantCulture);

                    try
                    {
                        var type = items[ScoreItemDatabasePropertyNames.Type];
                        if (type.S == ScoreItemDatabaseConstant.TypeImage)
                        {
                            var thumbnail          = items[ScoreItemDatabasePropertyNames.Thumbnail];
                            var thumbnailSizeValue =
                                thumbnail.M[ScoreItemDatabasePropertyNames.ThumbnailPropertyNames.Size];
                            size += long.Parse(thumbnailSizeValue.N, CultureInfo.InvariantCulture);
                        }
                    }
                    catch (Exception ex)
                    {
                        throw;
                    }


                    var itemIdText = items[ScoreItemDatabasePropertyNames.ItemId].S;

                    var item   = itemIdText.Substring(ScoreItemDatabaseConstant.ScoreIdLength);
                    var itemId = ScoreDatabaseUtils.ConvertToGuid(item);

                    return(itemIdText, itemId, size);
                }
            static async Task <DynamoDbScore> GetAsync(
                IAmazonDynamoDB client,
                string tableName,
                Guid ownerId,
                Guid scoreId)
            {
                var partitionKey = ScoreDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var request = new GetItemRequest()
                {
                    TableName = tableName,
                    Key       = new Dictionary <string, AttributeValue>()
                    {
                        [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                        [DynamoDbScorePropertyNames.SortKey]      =
                            new AttributeValue(ScoreDatabaseConstant.ScoreIdMainPrefix + score),
                    },
                };
                var response = await client.GetItemAsync(request);

                if (!response.IsItemSet)
                {
                    throw new NotFoundScoreException("Not found score.");
                }

                var dynamoDbScore = new DynamoDbScore(response.Item);

                return(dynamoDbScore);
            }
            static async Task <(DynamoDbScoreDataV1 data, string hash)> GetAsync(
                IAmazonDynamoDB client,
                string tableName,
                Guid ownerId,
                Guid scoreId)
            {
                var partitionKey = ScoreDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var request = new GetItemRequest()
                {
                    TableName = tableName,
                    Key       = new Dictionary <string, AttributeValue>()
                    {
                        [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                        [DynamoDbScorePropertyNames.SortKey]      =
                            new AttributeValue(ScoreDatabaseConstant.ScoreIdMainPrefix + score),
                    },
                };
                var response = await client.GetItemAsync(request);

                var data = response.Item[DynamoDbScorePropertyNames.Data];

                if (data is null)
                {
                    throw new InvalidOperationException("not found.");
                }


                DynamoDbScoreDataV1.TryMapFromAttributeValue(data, out var result);
                var hash = response.Item[DynamoDbScorePropertyNames.DataHash].S;

                return(result, hash);
            }
示例#7
0
        /// <summary>
        ///     DynamoDB のアイテムを変換する
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public static ScoreItemDatabaseItemDataBase ConvertFromDynamoDbValue(Dictionary <string, AttributeValue> item)
        {
            var ownerIdValue = item[ScoreItemDatabasePropertyNames.OwnerId];
            var itemIdValue  = item[ScoreItemDatabasePropertyNames.ItemId];
            var sizeValue    = item[ScoreItemDatabasePropertyNames.Size];

            var atValue = item[ScoreItemDatabasePropertyNames.At];

            var typeValue = item[ScoreItemDatabasePropertyNames.Type];

            var objNameValue = item[ScoreItemDatabasePropertyNames.ObjName];

            var totalSizeValue = item[ScoreItemDatabasePropertyNames.TotalSize];

            ScoreItemDatabaseItemDataBase result = default;

            if (typeValue.S == ScoreItemDatabaseConstant.TypeImage)
            {
                var orgNameValue   = item[ScoreItemDatabasePropertyNames.OrgName];
                var thumbnailValue = item[ScoreItemDatabasePropertyNames.Thumbnail];

                var thumbObjNameValue =
                    thumbnailValue.M[ScoreItemDatabasePropertyNames.ThumbnailPropertyNames.ObjName];
                var thumbSizeValue =
                    thumbnailValue.M[ScoreItemDatabasePropertyNames.ThumbnailPropertyNames.Size];

                result = new ScoreItemDatabaseItemDataImage
                {
                    OrgName   = orgNameValue.S,
                    Thumbnail = new ScoreItemDatabaseItemDataImageThumbnail
                    {
                        ObjName = thumbObjNameValue.S,
                        Size    = long.Parse(thumbSizeValue.N)
                    }
                };
            }
            else
            {
                throw new InvalidOperationException();
            }

            result.Size      = long.Parse(sizeValue.N);
            result.TotalSize = long.Parse(totalSizeValue.N);
            result.OwnerId   = ScoreItemDatabaseUtils.ConvertFromPartitionKey(ownerIdValue.S);

            var scoreBase64 = itemIdValue.S.Substring(0, ScoreItemDatabaseConstant.ScoreIdLength);
            var itemBase64  = itemIdValue.S.Substring(ScoreItemDatabaseConstant.ScoreIdLength);

            result.ScoreId = ScoreDatabaseUtils.ConvertToGuid(scoreBase64);
            result.ItemId  = ScoreDatabaseUtils.ConvertToGuid(itemBase64);
            result.ObjName = objNameValue.S;

            return(result);
        }
            static async Task UpdateAsync(
                IAmazonDynamoDB client,
                string tableName,
                Guid ownerId,
                Guid scoreId,
                int[] removeIndices,
                string newHash,
                string oldHash,
                DateTimeOffset now
                )
            {
                var partitionKey = ScoreDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var updateAt = ScoreDatabaseUtils.ConvertToUnixTimeMilli(now);

                var request = new UpdateItemRequest()
                {
                    Key = new Dictionary <string, AttributeValue>()
                    {
                        [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                        [DynamoDbScorePropertyNames.SortKey]      =
                            new AttributeValue(ScoreDatabaseConstant.ScoreIdMainPrefix + score),
                    },
                    ExpressionAttributeNames = new Dictionary <string, string>()
                    {
                        ["#updateAt"] = DynamoDbScorePropertyNames.UpdateAt,
                        ["#hash"]     = DynamoDbScorePropertyNames.DataHash,
                        ["#data"]     = DynamoDbScorePropertyNames.Data,
                        ["#pages"]    = DynamoDbScorePropertyNames.DataPropertyNames.Pages,
                    },
                    ExpressionAttributeValues = new Dictionary <string, AttributeValue>()
                    {
                        [":newHash"]  = new AttributeValue(newHash),
                        [":oldHash"]  = new AttributeValue(oldHash),
                        [":updateAt"] = new AttributeValue(updateAt),
                    },
                    ConditionExpression = "#hash = :oldHash",
                    UpdateExpression    =
                        $"SET #updateAt = :updateAt, #hash = :newHash REMOVE {string.Join(", ", removeIndices.Select(i=>$"#data.#pages[{i}]"))}",
                    TableName = tableName,
                };

                try
                {
                    await client.UpdateItemAsync(request);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }
        public async Task <(ScoreSnapshotDetail snapshot, ScoreAccesses access)> CreateSnapshotAsync(Guid ownerId,
                                                                                                     Guid scoreId,
                                                                                                     string snapshotName)
        {
            // TODO ここで作成されたデータを使い JSON ファイルを作成し S3 に保存する

            var snapshotId = Guid.NewGuid();
            var response   = await CreateSnapshotAsync(ownerId, scoreId, snapshotId, snapshotName);

            var snapshot = ScoreSnapshotDetail.Create(snapshotId, snapshotName, response.dynamoDbScore, response.hashSet);

            var access = ScoreDatabaseUtils.ConvertToScoreAccess(response.dynamoDbScore.Access);

            return(snapshot, access);
        }
示例#10
0
            static async Task DeleteItemRelations(IAmazonDynamoDB client, string tableName, Guid ownerId, Guid scoreId, HashSet <string> items)
            {
                var partitionKey = DynamoDbScoreItemRelationUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                const int chunkSize = 25;

                var chunkList = items.Select((x, index) => (x, index))
                                .GroupBy(x => x.index / chunkSize)
                                .Select(x => x.Select(y => y.x).ToArray())
                                .ToArray();

                foreach (var ids in chunkList)
                {
                    await DeleteItemRelations25Async(client, tableName, partitionKey, score, ids);
                }
示例#11
0
        CreateDynamoDbValue(ScoreItemDatabaseItemDataBase itemData, DateTimeOffset now)
        {
            var items = new Dictionary <string, AttributeValue>();

            var partitionKey = ScoreItemDatabaseUtils.ConvertToPartitionKey(itemData.OwnerId);
            var score        = ScoreDatabaseUtils.ConvertToBase64(itemData.ScoreId);
            var item         = ScoreDatabaseUtils.ConvertToBase64(itemData.ItemId);
            var at           = ScoreDatabaseUtils.ConvertToUnixTimeMilli(now);

            items[ScoreItemDatabasePropertyNames.OwnerId] = new AttributeValue(partitionKey);
            items[ScoreItemDatabasePropertyNames.ItemId]  = new AttributeValue(score + item);
            items[ScoreItemDatabasePropertyNames.ObjName] = new AttributeValue(itemData.ObjName);
            items[ScoreItemDatabasePropertyNames.Size]    = new AttributeValue {
                N = itemData.Size.ToString()
            };
            items[ScoreItemDatabasePropertyNames.At] = new AttributeValue(at);

            var totalSize = itemData.Size;

            if (itemData is ScoreItemDatabaseItemDataImage itemDataImage)
            {
                items[ScoreItemDatabasePropertyNames.Type] = new AttributeValue(ScoreItemDatabaseConstant.TypeImage);

                items[ScoreItemDatabasePropertyNames.OrgName] = new AttributeValue(itemDataImage.OrgName);

                items[ScoreItemDatabasePropertyNames.Thumbnail] = new AttributeValue
                {
                    M = new Dictionary <string, AttributeValue>
                    {
                        [ScoreItemDatabasePropertyNames.ThumbnailPropertyNames.ObjName] =
                            new AttributeValue(itemDataImage.Thumbnail.ObjName),
                        [ScoreItemDatabasePropertyNames.ThumbnailPropertyNames.Size] = new AttributeValue
                        {
                            N = itemDataImage.Thumbnail.Size.ToString()
                        }
                    }
                };

                totalSize += itemDataImage.Thumbnail.Size;
            }

            items[ScoreItemDatabasePropertyNames.TotalSize] = new AttributeValue {
                N = totalSize.ToString()
            };

            return(items, partitionKey, score, item, totalSize);
        }
示例#12
0
            static async Task <Dictionary <string, string> > GetAnnotationsAsync(
                IAmazonDynamoDB client, string tableName, Guid ownerId, Guid scoreId)
            {
                var partitionKey = DynamoDbScoreDataUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var request = new QueryRequest()
                {
                    TableName = tableName,
                    ExpressionAttributeNames = new Dictionary <string, string>()
                    {
                        ["#owner"]   = DynamoDbScoreDataPropertyNames.OwnerId,
                        ["#data"]    = DynamoDbScoreDataPropertyNames.DataId,
                        ["#content"] = DynamoDbScoreDataPropertyNames.Content,
                    },
                    ExpressionAttributeValues = new Dictionary <string, AttributeValue>()
                    {
                        [":owner"]    = new AttributeValue(partitionKey),
                        [":annScore"] = new AttributeValue(DynamoDbScoreDataConstant.PrefixAnnotation + score),
                    },
                    KeyConditionExpression = "#owner = :owner and begins_with(#data, :annScore)",
                    ProjectionExpression   = "#data, #content",
                };

                try
                {
                    var response = await client.QueryAsync(request);

                    var result = new Dictionary <string, string>();
                    var substringStartIndex = DynamoDbScoreDataConstant.PrefixAnnotation.Length + score.Length;
                    foreach (var item in response.Items)
                    {
                        var hashValue    = item[DynamoDbScoreDataPropertyNames.DataId];
                        var hash         = hashValue.S.Substring(substringStartIndex);
                        var contentValue = item[DynamoDbScoreDataPropertyNames.Content];
                        result[hash] = contentValue.S;
                    }

                    return(result);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }
示例#13
0
        public async Task DeleteItemRelationsAsync(Guid ownerId, Guid snapshotId, Guid[] itemIds)
        {
            const int chunkSize = 25;

            var partitionKey = DynamoDbScoreItemRelationUtils.ConvertToPartitionKey(ownerId);
            var snapshot     = ScoreDatabaseUtils.ConvertToBase64(snapshotId);

            var chunkList = itemIds
                            .Select((x, index) => (x: ScoreDatabaseUtils.ConvertToBase64(x) + snapshot, index))
                            .GroupBy(x => x.index / chunkSize)
                            .Select(x => x.Select(y => y.x).ToArray())
                            .ToArray();

            foreach (var ids in chunkList)
            {
                await DeleteData25Async(_dynamoDbClient, ScoreItemRelationTableName, partitionKey, ids);
            }
示例#14
0
        public async Task RemoveAnnotationsAsync(Guid ownerId, Guid scoreId, List <long> annotationIds)
        {
            if (annotationIds.Count == 0)
            {
                throw new ArgumentException(nameof(annotationIds));
            }

            var(data, oldHash) = await GetAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId);

            data.Annotations ??= new List <DynamoDbScoreAnnotationV1>();

            var existedIdSet = new HashSet <long>();

            annotationIds.ForEach(id => existedIdSet.Add(id));

            var removeIndices = data.Annotations.Select((x, index) => (x, index))
                                .Where(x => x.x != null && existedIdSet.Contains(x.x.Id))
                                .Select(x => x.index)
                                .ToArray();

            var removeHashSet = new HashSet <string>();

            foreach (var index in removeIndices.Reverse())
            {
                removeHashSet.Add(data.Annotations[index].ContentHash);
                data.Annotations.RemoveAt(index);
            }

            foreach (var annotation in data.Annotations)
            {
                if (removeHashSet.Contains(annotation.ContentHash))
                {
                    removeHashSet.Remove(annotation.ContentHash);
                }
            }

            var newHash = data.CalcDataHash();

            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            await UpdateAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId, removeIndices, newHash, oldHash, now);

            if (removeHashSet.Count != 0)
            {
                await RemoveDataAsync(_dynamoDbClient, ScoreDataTableName, ownerId, scoreId, removeHashSet);
            }
示例#15
0
            static async Task RemoveDataAsync(
                IAmazonDynamoDB client, string tableName, Guid ownerId, Guid scoreId,
                HashSet <string> removeHashSet)
            {
                const int chunkSize = 25;

                var partitionKey = DynamoDbScoreDataUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var chunkList = removeHashSet.Select((h, index) => (h, index))
                                .GroupBy(x => x.index / chunkSize)
                                .Select(x => x.Select(x => x.h).ToArray())
                                .ToArray();

                foreach (var hashList in chunkList)
                {
                    await RemoveData25Async(client, tableName, partitionKey, score, hashList);
                }
示例#16
0
        public async Task RemovePagesAsync(Guid ownerId, Guid scoreId, List <long> pageIds)
        {
            if (pageIds.Count == 0)
            {
                throw new ArgumentException(nameof(pageIds));
            }

            var(data, oldHash) = await GetAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId);

            data.Page ??= new List <DynamoDbScorePageV1>();

            var removeItemRelationSet = new HashSet <string>();

            foreach (var pageV1 in data.Page)
            {
                removeItemRelationSet.Add(pageV1.ItemId);
            }

            var existedIdSet = new HashSet <long>();

            pageIds.ForEach(id => existedIdSet.Add(id));

            var removeIndices = data.Page.Select((x, index) => (x, index))
                                .Where(x => x.x != null && existedIdSet.Contains(x.x.Id))
                                .Select(x => x.index)
                                .ToArray();

            foreach (var index in removeIndices.Reverse())
            {
                data.Page.RemoveAt(index);
            }

            foreach (var pageV1 in data.Page)
            {
                removeItemRelationSet.Remove(pageV1.ItemId);
            }

            var newHash = data.CalcDataHash();

            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            await UpdateAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId, removeIndices, newHash, oldHash, now);

            await DeleteItemRelations(_dynamoDbClient, ScoreItemRelationTableName, ownerId, scoreId, removeItemRelationSet);
示例#17
0
        public static ScoreDetail Create(DynamoDbScore score, Dictionary <string, string> hashSet)
        {
            if (score.Type != DynamoDbScoreTypes.Main)
            {
                throw new ArgumentException(nameof(score));
            }

            var data = ScoreData.Create(score.Data);

            return(new ScoreDetail()
            {
                CreateAt = ScoreDatabaseUtils.ConvertFromUnixTimeMilli(score.CreateAt),
                UpdateAt = ScoreDatabaseUtils.ConvertFromUnixTimeMilli(score.UpdateAt),
                DataHash = score.DataHash,
                Data = data,
                Access = ScoreDatabaseUtils.ConvertToScoreAccess(score.Access),
                HashSet = hashSet.ToDictionary(x => x.Key, x => x.Value),
            });
        }
示例#18
0
 public static ScoreData Create(DynamoDbScoreDataV1 data)
 {
     return(new ScoreData()
     {
         Title = data.Title,
         DescriptionHash = data.DescriptionHash,
         Pages = data.Page.Select(x => new ScorePage()
         {
             Id = x.Id,
             Page = x.Page,
             ItemId = ScoreDatabaseUtils.ConvertToGuid(x.ItemId),
             ObjectName = x.ObjectName,
         }).ToArray(),
         Annotations = data.Annotations.Select(x => new ScoreAnnotation()
         {
             Id = x.Id,
             ContentHash = x.ContentHash,
         }).ToArray(),
     });
 }
示例#19
0
            static async Task <(bool success, string description)> TryGetDescriptionAsync(
                IAmazonDynamoDB client, string tableName, Guid ownerId, Guid scoreId, string descriptionHash)
            {
                var partitionKey = DynamoDbScoreDataUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var request = new GetItemRequest()
                {
                    TableName = tableName,
                    Key       = new Dictionary <string, AttributeValue>()
                    {
                        [DynamoDbScoreDataPropertyNames.OwnerId] = new AttributeValue(partitionKey),
                        [DynamoDbScoreDataPropertyNames.DataId]  = new AttributeValue(DynamoDbScoreDataConstant.PrefixDescription + score + descriptionHash),
                    },
                    ExpressionAttributeNames = new Dictionary <string, string>()
                    {
                        ["#content"] = DynamoDbScoreDataPropertyNames.Content,
                    },
                    ProjectionExpression = "#content",
                };

                try
                {
                    var response = await client.GetItemAsync(request);

                    if (!response.IsItemSet)
                    {
                        return(false, "");
                    }

                    var description = response.Item[DynamoDbScoreDataPropertyNames.Content].S;
                    return(true, description);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }
示例#20
0
        public async Task AddPagesAsync(Guid ownerId, Guid scoreId, List <NewScorePage> pages)
        {
            if (pages.Count == 0)
            {
                throw new ArgumentException(nameof(pages));
            }

            var(data, oldHash) = await GetAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId);

            data.Page ??= new List <DynamoDbScorePageV1>();

            var newPages           = new List <DynamoDbScorePageV1>();
            var newItemRelationSet = new HashSet <string>();

            var pageId = data.Page.Count == 0 ? 0 : data.Page.Select(x => x.Id).Max() + 1;

            foreach (var page in pages)
            {
                var itemId = ScoreDatabaseUtils.ConvertToBase64(page.ItemId);
                var p      = new DynamoDbScorePageV1()
                {
                    Id         = pageId++,
                    ItemId     = itemId,
                    Page       = page.Page,
                    ObjectName = page.ObjectName,
                };
                newPages.Add(p);
                data.Page.Add(p);
                newItemRelationSet.Add(itemId);
            }

            var newHash = data.CalcDataHash();

            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            // TODO ページの追加上限値判定を追加
            await UpdateAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId, newPages, newHash, oldHash, now);

            await PutItemRelations(_dynamoDbClient, ScoreItemRelationTableName, ownerId, scoreId, newItemRelationSet);
示例#21
0
            static async Task DeleteItemAsync(
                IAmazonDynamoDB client,
                string tableName,
                Guid ownerId,
                Guid scoreId,
                Guid snapshotId
                )
            {
                var partitionKey = ScoreDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);
                var snapshot     = ScoreDatabaseUtils.ConvertToBase64(snapshotId);

                var actions = new List <TransactWriteItem>()
                {
                    new TransactWriteItem()
                    {
                        Delete = new Delete()
                        {
                            TableName = tableName,
                            Key       = new Dictionary <string, AttributeValue>()
                            {
                                [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                                [DynamoDbScorePropertyNames.SortKey]      = new AttributeValue(ScoreDatabaseConstant.ScoreIdSnapPrefix + score + snapshot),
                            },
                            ExpressionAttributeNames = new Dictionary <string, string>()
                            {
                                ["#score"] = DynamoDbScorePropertyNames.SortKey,
                            },
                            ConditionExpression = "attribute_exists(#score)",
                        },
                    },
                    new TransactWriteItem()
                    {
                        Update = new Update()
                        {
                            TableName = tableName,
                            Key       = new Dictionary <string, AttributeValue>()
                            {
                                [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                                [DynamoDbScorePropertyNames.SortKey]      = new AttributeValue(ScoreDatabaseConstant.ScoreIdMainPrefix + score),
                            },
                            ExpressionAttributeNames = new Dictionary <string, string>()
                            {
                                ["#snapshotCount"] = DynamoDbScorePropertyNames.SnapshotCount,
                            },
                            ExpressionAttributeValues = new Dictionary <string, AttributeValue>()
                            {
                                [":increment"] = new AttributeValue()
                                {
                                    N = "-1"
                                },
                            },
                            UpdateExpression = "ADD #snapshotCount :increment",
                        }
                    },
                };

                try
                {
                    await client.TransactWriteItemsAsync(new TransactWriteItemsRequest()
                    {
                        TransactItems          = actions,
                        ReturnConsumedCapacity = ReturnConsumedCapacity.TOTAL,
                    });
                }
                catch (TransactionCanceledException ex)
                {
                    var deleteReason = ex.CancellationReasons[0];

                    if (deleteReason.Code == "ConditionalCheckFailed")
                    {
                        throw new NotFoundSnapshotException(ex);
                    }

                    throw;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }
            static async Task UpdateAsync(
                IAmazonDynamoDB client,
                string tableName,
                Guid ownerId,
                Guid scoreId,
                Guid snapshotId,
                string snapshotName,
                DateTimeOffset now,
                int maxSnapshotCount
                )
            {
                var partitionKey = ScoreDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);
                var snapshot     = ScoreDatabaseUtils.ConvertToBase64(snapshotId);

                var at = ScoreDatabaseUtils.ConvertToUnixTimeMilli(now);

                var actions = new List <TransactWriteItem>()
                {
                    new TransactWriteItem()
                    {
                        Update = new Update()
                        {
                            TableName = tableName,
                            Key       = new Dictionary <string, AttributeValue>()
                            {
                                [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                                [DynamoDbScorePropertyNames.SortKey]      = new AttributeValue(ScoreDatabaseConstant.ScoreIdMainPrefix + score),
                            },
                            ExpressionAttributeNames = new Dictionary <string, string>()
                            {
                                ["#snapshotCount"] = DynamoDbScorePropertyNames.SnapshotCount,
                            },
                            ExpressionAttributeValues = new Dictionary <string, AttributeValue>()
                            {
                                [":increment"] = new AttributeValue()
                                {
                                    N = "1"
                                },
                                [":countMax"] = new AttributeValue()
                                {
                                    N = maxSnapshotCount.ToString(),
                                },
                            },
                            ConditionExpression = "#snapshotCount < :countMax",
                            UpdateExpression    = "ADD #snapshotCount :increment"
                        }
                    },
                    new TransactWriteItem()
                    {
                        Put = new Put()
                        {
                            TableName = tableName,
                            Item      = new Dictionary <string, AttributeValue>()
                            {
                                [DynamoDbScorePropertyNames.PartitionKey] = new AttributeValue(partitionKey),
                                [DynamoDbScorePropertyNames.SortKey]      = new AttributeValue(ScoreDatabaseConstant.ScoreIdSnapPrefix + score + snapshot),
                                [DynamoDbScorePropertyNames.CreateAt]     = new AttributeValue(at),
                                [DynamoDbScorePropertyNames.UpdateAt]     = new AttributeValue(at),
                                [DynamoDbScorePropertyNames.SnapshotName] = new AttributeValue(snapshotName),
                            },
                            ExpressionAttributeNames = new Dictionary <string, string>()
                            {
                                ["#score"] = DynamoDbScorePropertyNames.SortKey,
                            },
                            ConditionExpression = "attribute_not_exists(#score)",
                        }
                    }
                };

                try
                {
                    await client.TransactWriteItemsAsync(new TransactWriteItemsRequest()
                    {
                        TransactItems          = actions,
                        ReturnConsumedCapacity = ReturnConsumedCapacity.TOTAL
                    });
                }
                catch (TransactionCanceledException ex)
                {
                    var updateReason = ex.CancellationReasons[0];

                    if (updateReason.Code == "ConditionalCheckFailed")
                    {
                        throw new CreatedSnapshotException(CreatedSnapshotExceptionCodes.ExceededUpperLimit, ex);
                    }

                    throw;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }
示例#23
0
 /// <summary>
 /// 楽譜のアイテムデータのパーティションキーから UUID に変換する
 /// </summary>
 /// <param name="partitionKey"></param>
 /// <returns></returns>
 /// <exception cref="NotImplementedException"></exception>
 public static Guid ConvertFromPartitionKey(string partitionKey) =>
 ScoreDatabaseUtils.ConvertToGuid(
     partitionKey.Substring(ScoreItemDatabaseConstant.PartitionKeyPrefix.Length));
示例#24
0
 /// <summary>
 /// 楽譜のアイテムデータのパーティションキー
 /// </summary>
 /// <param name="ownerId"></param>
 /// <returns></returns>
 public static string ConvertToPartitionKey(Guid ownerId) => ScoreItemDatabaseConstant.PartitionKeyPrefix +
 ScoreDatabaseUtils.ConvertToBase64(ownerId);
示例#25
0
        public async Task ReplacePagesAsync(Guid ownerId, Guid scoreId, List <PatchScorePage> pages)
        {
            if (pages.Count == 0)
            {
                throw new ArgumentException(nameof(pages));
            }

            var(data, oldHash) = await GetAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId);

            data.Page ??= new List <DynamoDbScorePageV1>();

            var removeRelationItemSet = new HashSet <string>();
            var newRelationItemSet    = new HashSet <string>();

            foreach (var pageV1 in data.Page)
            {
                removeRelationItemSet.Add(pageV1.ItemId);
            }

            // Key id, Value index
            var pageIndices = new Dictionary <long, int>();

            foreach (var(databaseScoreDataPageV1, index) in data.Page.Select((x, index) => (x, index)))
            {
                pageIndices[databaseScoreDataPageV1.Id] = index;
            }

            var replacingPages = new List <(DynamoDbScorePageV1 data, int targetIndex)>();

            foreach (var page in pages)
            {
                var id = page.TargetPageId;
                if (!pageIndices.TryGetValue(id, out var index))
                {
                    throw new InvalidOperationException();
                }

                var itemId = ScoreDatabaseUtils.ConvertToBase64(page.ItemId);
                var p      = new DynamoDbScorePageV1()
                {
                    Id         = id,
                    ItemId     = itemId,
                    Page       = page.Page,
                    ObjectName = page.ObjectName,
                };
                replacingPages.Add((p, index));
                data.Page[index] = p;
                newRelationItemSet.Add(itemId);
            }

            foreach (var pageV1 in data.Page)
            {
                removeRelationItemSet.Remove(pageV1.ItemId);
            }

            var newHash = data.CalcDataHash();

            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            await UpdateAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId, replacingPages, newHash, oldHash, now);

            await PutItemRelations(_dynamoDbClient, ScoreItemRelationTableName, ownerId, scoreId, newRelationItemSet);

            await DeleteItemRelations(_dynamoDbClient, ScoreItemRelationTableName, ownerId, scoreId, removeRelationItemSet);
示例#26
0
        public async Task ReplaceAnnotationsAsync(Guid ownerId, Guid scoreId, List <PatchScoreAnnotation> annotations)
        {
            if (annotations.Count == 0)
            {
                throw new ArgumentException(nameof(annotations));
            }

            var(data, oldHash) = await GetAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId);

            data.Annotations ??= new List <DynamoDbScoreAnnotationV1>();

            // Key id, Value index
            var annotationIndices = new Dictionary <long, int>();

            foreach (var(ann, index) in data.Annotations.Select((x, index) => (x, index)))
            {
                annotationIndices[ann.Id] = index;
            }

            var replacingAnnotations = new List <(DynamoDbScoreAnnotationV1 data, int targetIndex)>();

            var existedAnnData = new HashSet <string>();

            data.Annotations.ForEach(x => existedAnnData.Add(x.ContentHash));
            var addAnnData = new Dictionary <string, PatchScoreAnnotation>();

            foreach (var ann in annotations)
            {
                var id = ann.TargetAnnotationId;
                if (!annotationIndices.TryGetValue(id, out var index))
                {
                    throw new InvalidOperationException();
                }

                var hash = DynamoDbScoreDataUtils.CalcHash(DynamoDbScoreDataUtils.AnnotationPrefix, ann.Content);

                if (!existedAnnData.Contains(hash))
                {
                    addAnnData[hash] = ann;
                }

                var a = new DynamoDbScoreAnnotationV1()
                {
                    Id          = id,
                    ContentHash = hash,
                };
                replacingAnnotations.Add((a, index));
                data.Annotations[index] = a;
            }

            var removeAnnData = existedAnnData.ToHashSet();

            foreach (var annotation in data.Annotations)
            {
                if (removeAnnData.Contains(annotation.ContentHash))
                {
                    removeAnnData.Remove(annotation.ContentHash);
                }
            }

            var newHash = data.CalcDataHash();

            var now = ScoreDatabaseUtils.UnixTimeMillisecondsNow();

            await UpdateAsync(_dynamoDbClient, ScoreTableName, ownerId, scoreId, replacingAnnotations, newHash, oldHash, now);

            await AddAnnListAsync(_dynamoDbClient, ScoreDataTableName, ownerId, scoreId, addAnnData);
            await RemoveAnnListAsync(_dynamoDbClient, ScoreDataTableName, ownerId, scoreId, removeAnnData);
示例#27
0
            static async Task <ScoreSnapshotSummary[]> GetAsync(IAmazonDynamoDB client, string tableName, Guid ownerId, Guid scoreId)
            {
                var partitionKey = ScoreDatabaseUtils.ConvertToPartitionKey(ownerId);
                var score        = ScoreDatabaseUtils.ConvertToBase64(scoreId);

                var request = new QueryRequest()
                {
                    TableName = tableName,
                    ExpressionAttributeNames = new Dictionary <string, string>()
                    {
                        ["#owner"]        = DynamoDbScorePropertyNames.PartitionKey,
                        ["#score"]        = DynamoDbScorePropertyNames.SortKey,
                        ["#snapshotName"] = DynamoDbScorePropertyNames.SnapshotName,
                        ["#createAt"]     = DynamoDbScorePropertyNames.CreateAt,
                    },
                    ExpressionAttributeValues = new Dictionary <string, AttributeValue>()
                    {
                        [":owner"]      = new AttributeValue(partitionKey),
                        [":snapPrefix"] = new AttributeValue(ScoreDatabaseConstant.ScoreIdSnapPrefix + score),
                    },
                    KeyConditionExpression = "#owner = :owner and begins_with(#score, :snapPrefix)",
                    ProjectionExpression   = "#score, #snapshotName, #createAt",
                };

                try
                {
                    var response = await client.QueryAsync(request);

                    var subStartIndex = (ScoreDatabaseConstant.ScoreIdSnapPrefix + score).Length;

                    return(response.Items
                           .Select(x => (
                                       score: x[DynamoDbScorePropertyNames.SortKey].S,
                                       name: x[DynamoDbScorePropertyNames.SnapshotName].S,
                                       createAt: x[DynamoDbScorePropertyNames.CreateAt].S)
                                   )
                           .Select(x =>
                                   new ScoreSnapshotSummary()
                    {
                        Id = ScoreDatabaseUtils.ConvertToGuid(x.score.Substring(subStartIndex)),
                        Name = x.name,
                        CreateAt = ScoreDatabaseUtils.ConvertFromUnixTimeMilli(x.createAt),
                    }
                                   )
                           .ToArray());
                }
                catch (InternalServerErrorException ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
                catch (ProvisionedThroughputExceededException ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
                catch (RequestLimitExceededException ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
                catch (ResourceNotFoundException ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    throw;
                }
            }