Skip to content

Commit

Permalink
feat(appsync): support for read consistency during DynamoDB reads (#2…
Browse files Browse the repository at this point in the history
…0793)

----
Motivation: DynamoDB uses eventually consistent reads unless you specify otherwise. Now the users cannot specify consistent read during DynamoDB reads. This commit allows users to set consistent read while creating resolvers.

Use case: To control the amount of replicas contacted during DynamoDB reads.

most salient design aspects: 
Added an boolean parameter to the dynamoDbGetItem, dynamoDbScanTable and dynamoDbQuery methods in mapping-template.ts. Changed relative tests.

closes #5980

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features
I ran yarn tests. They include integration test snapshots for DynamoDB reads. Do I need to write a new integration test?
* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?
*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
YichenQian09 committed Jul 21, 2022
1 parent eed854e commit 0b911ef
Show file tree
Hide file tree
Showing 33 changed files with 2,369 additions and 209 deletions.
12 changes: 12 additions & 0 deletions packages/@aws-cdk/aws-appsync/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ const demoTable = new dynamodb.Table(this, 'DemoTable', {
const demoDS = api.addDynamoDbDataSource('demoDataSource', demoTable);

// Resolver for the Query "getDemos" that scans the DynamoDb table and returns the entire list.
// Resolver Mapping Template Reference:
// https://docs.aws.amazon.com/appsync/latest/devguide/resolver-mapping-template-reference-dynamodb.html
demoDS.createResolver({
typeName: 'Query',
fieldName: 'getDemos',
Expand All @@ -94,8 +96,18 @@ demoDS.createResolver({
),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultItem(),
});

//To enable DynamoDB read consistency with the `MappingTemplate`:
demoDS.createResolver({
typeName: 'Query',
fieldName: 'getDemosConsistent',
requestMappingTemplate: appsync.MappingTemplate.dynamoDbScanTable(true),
responseMappingTemplate: appsync.MappingTemplate.dynamoDbResultList(),
});
```



### Aurora Serverless

AppSync provides a data source for executing SQL commands against Amazon Aurora
Expand Down
12 changes: 6 additions & 6 deletions packages/@aws-cdk/aws-appsync/lib/mapping-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ export abstract class MappingTemplate {
/**
* Mapping template to scan a DynamoDB table to fetch all entries
*/
public static dynamoDbScanTable(): MappingTemplate {
return this.fromString('{"version" : "2017-02-28", "operation" : "Scan"}');
public static dynamoDbScanTable(consistentRead: boolean = false): MappingTemplate {
return this.fromString(`{"version" : "2017-02-28", "operation" : "Scan", "consistentRead": ${consistentRead}}`);
}

/**
* Mapping template to query a set of items from a DynamoDB table
*
* @param cond the key condition for the query
*/
public static dynamoDbQuery(cond: KeyCondition, indexName?: string): MappingTemplate {
return this.fromString(`{"version" : "2017-02-28", "operation" : "Query", ${indexName ? `"index" : "${indexName}", ` : ''}${cond.renderTemplate()}}`);
public static dynamoDbQuery(cond: KeyCondition, indexName?: string, consistentRead: boolean = false): MappingTemplate {
return this.fromString(`{"version" : "2017-02-28", "operation" : "Query", "consistentRead": ${consistentRead}, ${indexName ? `"index" : "${indexName}", ` : ''}${cond.renderTemplate()}}`);
}

/**
Expand All @@ -55,8 +55,8 @@ export abstract class MappingTemplate {
* @param keyName the name of the hash key field
* @param idArg the name of the Query argument
*/
public static dynamoDbGetItem(keyName: string, idArg: string): MappingTemplate {
return this.fromString(`{"version": "2017-02-28", "operation": "GetItem", "key": {"${keyName}": $util.dynamodb.toDynamoDBJson($ctx.args.${idArg})}}`);
public static dynamoDbGetItem(keyName: string, idArg: string, consistentRead: boolean = false): MappingTemplate {
return this.fromString(`{"version": "2017-02-28", "operation": "GetItem", "consistentRead": ${consistentRead}, "key": {"${keyName}": $util.dynamodb.toDynamoDBJson($ctx.args.${idArg})}}`);
}

/**
Expand Down
19 changes: 19 additions & 0 deletions packages/@aws-cdk/aws-appsync/test/appsync-dynamodb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Template } from '@aws-cdk/assertions';
import * as db from '@aws-cdk/aws-dynamodb';
import * as cdk from '@aws-cdk/core';
import * as appsync from '../lib';
import { KeyCondition } from '../lib';

function joined(str: string): string {
return str.replace(/\s+/g, '');
Expand Down Expand Up @@ -80,6 +81,24 @@ describe('DynamoDb Data Source configuration', () => {
});

describe('DynamoDB Mapping Templates', () => {
test('read consistency option for dynamoDbScanTable should render correctly', () => {
const template = appsync.MappingTemplate.dynamoDbScanTable(true);
const rendered = joined(template.renderTemplate());
expect(rendered).toStrictEqual('{\"version\":\"2017-02-28\",\"operation\":\"Scan\",\"consistentRead\":true}');
});

test('read consistency option for dynamoDbGetItem should render correctly', () => {
const template = appsync.MappingTemplate.dynamoDbGetItem('id', 'id', true);
const rendered = joined(template.renderTemplate());
expect(rendered).toStrictEqual('{\"version\":\"2017-02-28\",\"operation\":\"GetItem\",\"consistentRead\":true,\"key\":{\"id\":$util.dynamodb.toDynamoDBJson($ctx.args.id)}}');
});

test('read consistency option for dynamoDbQuery should render correctly', () => {
const template = appsync.MappingTemplate.dynamoDbQuery(KeyCondition.eq('order', 'order'), 'orderIndex', true);
const rendered = joined(template.renderTemplate());
expect(rendered).toStrictEqual('{\"version\":\"2017-02-28\",\"operation\":\"Query\",\"consistentRead\":true,\"index\":\"orderIndex\",\"query\":{\"expression\":\"#order=:order\",\"expressionNames\":{\"#order\":\"order\"},\"expressionValues\":{\":order\":$util.dynamodb.toDynamoDBJson($ctx.args.order)}}}');
});

test('PutItem projecting all', () => {
const template = appsync.MappingTemplate.dynamoDbPutItem(
appsync.PrimaryKey.partition('id').is('id'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "20.0.0",
"files": {
"edb3fd1d912b400f4857c41a3ff80bcc32d180595affcd6c017f72afd5959482": {
"7eaa4499d04a6696121796bb6e27fab5b4211517a6e0523260984e33c134f84f": {
"source": {
"path": "aws-appsync-integ.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "edb3fd1d912b400f4857c41a3ff80bcc32d180595affcd6c017f72afd5959482.json",
"objectKey": "7eaa4499d04a6696121796bb6e27fab5b4211517a6e0523260984e33c134f84f.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
"TypeName": "Query",
"DataSourceName": "testDataSource",
"Kind": "UNIT",
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
"ResponseMappingTemplate": "$util.toJson($ctx.result.items)"
},
"DependsOn": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.CfnGraphQLApi",
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
},
Expand All @@ -51,7 +51,7 @@
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.CfnGraphQLSchema",
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
},
Expand All @@ -70,7 +70,7 @@
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.CfnApiKey",
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
},
Expand Down Expand Up @@ -206,7 +206,7 @@
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.CfnDataSource",
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
},
Expand All @@ -230,19 +230,19 @@
"typeName": "Query",
"dataSourceName": "testDataSource",
"kind": "UNIT",
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
"responseMappingTemplate": "$util.toJson($ctx.result.items)"
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.CfnResolver",
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.Resolver",
"version": "0.0.0"
"fqn": "constructs.Construct",
"version": "10.1.33"
}
},
"MutationaddTestResolver": {
Expand Down Expand Up @@ -270,25 +270,25 @@
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.CfnResolver",
"fqn": "@aws-cdk/core.CfnResource",
"version": "0.0.0"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.Resolver",
"version": "0.0.0"
"fqn": "constructs.Construct",
"version": "10.1.33"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.DynamoDbDataSource",
"version": "0.0.0"
"fqn": "constructs.Construct",
"version": "10.1.33"
}
}
},
"constructInfo": {
"fqn": "@aws-cdk/aws-appsync.GraphqlApi",
"fqn": "@aws-cdk/core.Resource",
"version": "0.0.0"
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
}
}
},
"4d9e950f40fbc075b913a37030dfd9c31de11a2230d2c5c7e45b8b9730faaa3f": {
"c06c582f69e71eae955ab50693deb45f0836b7837ef3184a737f87c4bf7952a4": {
"source": {
"path": "aws-appsync-integ.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "4d9e950f40fbc075b913a37030dfd9c31de11a2230d2c5c7e45b8b9730faaa3f.json",
"objectKey": "c06c582f69e71eae955ab50693deb45f0836b7837ef3184a737f87c4bf7952a4.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@
"TypeName": "Query",
"DataSourceName": "testDataSource",
"Kind": "UNIT",
"RequestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
"RequestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"consistentRead\": false, \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
"ResponseMappingTemplate": "$util.toJson($ctx.result)"
},
"DependsOn": [
Expand All @@ -187,7 +187,7 @@
"TypeName": "Query",
"DataSourceName": "testDataSource",
"Kind": "UNIT",
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
"ResponseMappingTemplate": "$util.toJson($ctx.result.items)"
},
"DependsOn": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.0.9"
"version": "10.1.33"
}
},
"aws-appsync-integ": {
Expand Down Expand Up @@ -273,7 +273,7 @@
"typeName": "Query",
"dataSourceName": "testDataSource",
"kind": "UNIT",
"requestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
"requestMappingTemplate": "{\"version\": \"2017-02-28\", \"operation\": \"GetItem\", \"consistentRead\": false, \"key\": {\"id\": $util.dynamodb.toDynamoDBJson($ctx.args.id)}}",
"responseMappingTemplate": "$util.toJson($ctx.result)"
}
},
Expand Down Expand Up @@ -308,7 +308,7 @@
"typeName": "Query",
"dataSourceName": "testDataSource",
"kind": "UNIT",
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
"responseMappingTemplate": "$util.toJson($ctx.result.items)"
}
},
Expand Down Expand Up @@ -714,13 +714,13 @@
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.0.9"
"version": "10.1.33"
}
}
},
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.0.9"
"version": "10.1.33"
}
},
"testFail": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"17.0.0"}
{"version":"20.0.0"}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "17.0.0",
"version": "20.0.0",
"files": {
"13205a5206cf49ce1c9674807fa5b2c3315de95159c5c1c969664271edd96507": {
"56d1a7b725a78aa83ffacc3ba1d3ae4b4c2fa61c3caff19dafd5f77c1e0333c3": {
"source": {
"path": "code-first-schema.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "13205a5206cf49ce1c9674807fa5b2c3315de95159c5c1c969664271edd96507.json",
"objectKey": "56d1a7b725a78aa83ffacc3ba1d3ae4b4c2fa61c3caff19dafd5f77c1e0333c3.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
"TypeName": "Query",
"DataSourceName": "planets",
"Kind": "UNIT",
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
"RequestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
"ResponseMappingTemplate": "$util.toJson($ctx.result.items)"
},
"DependsOn": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "17.0.0",
"version": "20.0.0",
"artifacts": {
"Tree": {
"type": "cdk:tree",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"path": "Tree",
"constructInfo": {
"fqn": "constructs.Construct",
"version": "10.0.9"
"version": "10.1.33"
}
},
"code-first-schema": {
Expand Down Expand Up @@ -236,7 +236,7 @@
"typeName": "Query",
"dataSourceName": "planets",
"kind": "UNIT",
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\"}",
"requestMappingTemplate": "{\"version\" : \"2017-02-28\", \"operation\" : \"Scan\", \"consistentRead\": false}",
"responseMappingTemplate": "$util.toJson($ctx.result.items)"
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "17.0.0",
"version": "20.0.0",
"files": {
"7b4937a061e73c53a8d1ae8eeb6c778db65cfc02df7a34947b816bcd1332f9c9": {
"410a11ff94f45720614bec412ef8a8f4aa68e8f98e181bbd4b98fb47cbb76284": {
"source": {
"path": "aws-appsync-integ.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "7b4937a061e73c53a8d1ae8eeb6c778db65cfc02df7a34947b816bcd1332f9c9.json",
"objectKey": "410a11ff94f45720614bec412ef8a8f4aa68e8f98e181bbd4b98fb47cbb76284.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Loading

0 comments on commit 0b911ef

Please sign in to comment.