DynamoDB consistent reads for Global Secondary Index

江枫思渺然 提交于 2019-12-04 06:15:23

You probably already went through this: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html

The short answer is that you cannot do what you want to do with Global Secondary Indexes (ie it's always eventual consistency).

A solution here would be to have a separate table w/ the attribute you're interested in as a key and do consistent reads there. You would need to ensure you are updating that whenever you are inserting new entities, and you would also have to worry about the edge case in which inserts there succeed, but not in the main table (ie you need to ensure they are in sync)

Another solution would be to scan the whole table, but that would probably be overkill if the table is large.

Why do you care if somebody creates 2 accounts with the same email? You could just use the username as the primary hash key and just not enforce the email uniqueness.

Depending on your situation and considering all of the alternatives, it may be acceptable to add an automatic retry when you don't find anything on the GSI the first time to work around the lack of strongly consistent reads. I didn't even think of this until I hit road blocks with other options and then realized this was simple and didn't cause any issues for our particular use case.

{
"TableName": "tokens",

"ProvisionedThroughput": { "ReadCapacityUnits": 5, "WriteCapacityUnits": 5 },

"AttributeDefinitions": [
    { "AttributeName": "data", "AttributeType": "S" },
    { "AttributeName": "type", "AttributeType": "S" },
    { "AttributeName": "token", "AttributeType": "S" }
],

"KeySchema": [
    { "AttributeName": "data", "KeyType": "HASH" },
    { "AttributeName": "type", "KeyType": "RANGE" }
],

"GlobalSecondaryIndexes": [
    {
        "IndexName": "tokens-token",

        "KeySchema": [
            { "AttributeName": "token", "KeyType": "HASH" }
        ],

        "Projection": {
            "ProjectionType": "ALL"
        },

        "ProvisionedThroughput": { "ReadCapacityUnits": 2, "WriteCapacityUnits": 2 }
    }
],

"SSESpecification":  {"Enabled": true }

}

    public async getByToken(token: string): Promise<TokenResponse> {
    let tokenResponse: TokenResponse;
    let tries = 1;
    while (tries <= 2) { // Can't perform strongly consistent read on GSI so we have to do this to insure the token doesn't exist
        let item = await this.getItemByToken(token);
        if (item) return new TokenResponse(item);
        if (tries == 1) await this.sleep(1000);
        tries++;
    }
    return tokenResponse;
}

Since we don't care about performance for someone sending in a non-existent token (which should never happen anyway), we work around the problem without taking any performance hit (other than a possible 1 second delay one time after the token is created). If you just created the token, you wouldn't need to resolve it back to the data you just passed in. But if you happen to do that, we handle it transparently.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!