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);
            }
            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);
            }
Beispiel #3
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;
                }
            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;
                }
            }
            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);
                }
Beispiel #6
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);
        }
            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;
                }
            }
Beispiel #8
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);
            }
Beispiel #9
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);
                }
            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;
                }
            }
Beispiel #11
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);
        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);
Beispiel #13
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;
                }
            }
Beispiel #14
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;
                }
            }
Beispiel #15
0
 /// <summary>
 /// 楽譜のアイテムデータのパーティションキー
 /// </summary>
 /// <param name="ownerId"></param>
 /// <returns></returns>
 public static string ConvertToPartitionKey(Guid ownerId) => ScoreItemDatabaseConstant.PartitionKeyPrefix +
 ScoreDatabaseUtils.ConvertToBase64(ownerId);
            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;
                }
            }