From c98b191ac68040c5539593e8a71fa2c572467255 Mon Sep 17 00:00:00 2001 From: "A. Ruiz" <40408539+maestre3d@users.noreply.github.com> Date: Mon, 24 Apr 2023 01:37:32 -0600 Subject: [PATCH] Add Amazon DynamoDB deduplication storage implementation --- driver/dynamodb/.gitignore | 1 + driver/dynamodb/Makefile | 2 + driver/dynamodb/README.md | 63 +++++++++- driver/dynamodb/deduplication_storage.go | 102 ++++++++++++++++ driver/dynamodb/deduplication_storage_test.go | 115 ++++++++++++++++++ driver/dynamodb/docker-compose.yml | 11 ++ driver/dynamodb/go.mod | 41 +++++++ driver/dynamodb/go.sum | 88 ++++++++++++++ driver/dynamodb/reader.go | 1 - driver/dynamodb/writer.go | 1 - 10 files changed, 418 insertions(+), 7 deletions(-) create mode 100644 driver/dynamodb/.gitignore create mode 100644 driver/dynamodb/Makefile create mode 100644 driver/dynamodb/deduplication_storage.go create mode 100644 driver/dynamodb/deduplication_storage_test.go create mode 100644 driver/dynamodb/docker-compose.yml create mode 100644 driver/dynamodb/go.sum delete mode 100644 driver/dynamodb/reader.go delete mode 100644 driver/dynamodb/writer.go diff --git a/driver/dynamodb/.gitignore b/driver/dynamodb/.gitignore new file mode 100644 index 0000000..cdb0bc3 --- /dev/null +++ b/driver/dynamodb/.gitignore @@ -0,0 +1 @@ +shared-local-instance.db \ No newline at end of file diff --git a/driver/dynamodb/Makefile b/driver/dynamodb/Makefile new file mode 100644 index 0000000..4e7a01e --- /dev/null +++ b/driver/dynamodb/Makefile @@ -0,0 +1,2 @@ +run-integration-tests: + go test -v ./... -coverprofile coverage.out -tags=integration diff --git a/driver/dynamodb/README.md b/driver/dynamodb/README.md index fb2b9db..3dd8e5e 100644 --- a/driver/dynamodb/README.md +++ b/driver/dynamodb/README.md @@ -1,6 +1,9 @@ # Streams Driver for Amazon DynamoDB -The **stream driver** for `Amazon DynamoDB` offers a `Writer` implementation to be used by systems implementing the +The **stream driver** for `Amazon DynamoDB` which offers a deduplication storage implementation to ensure idempotency +for message processing. + +It is planned to offer in a near future a `Writer` implementation to be used by systems implementing the _**transactional outbox**_ messaging pattern. Moreover, the `Message Egress Proxy` (_aka. log trailing_) component could be used along this driver to @@ -10,14 +13,64 @@ Furthermore, `Amazon DynamoDB` has a **_Change-Data-Capture stream feature_** re services such as _Lambda and Kinesis_. Thus, this feature could be combined along the `Message Egress Proxy` component in order to stream messages into desired infrastructure non-supported by `Amazon DynamoDB Stream` feature. -## Requirements +## Deduplication Storage Requirements + +In order for this driver to work, the database MUST have a deduplication table with the following schema. + +```json +{ + "TableName": "deduplication-table", + "KeySchema": [ + { + "KeyType": "HASH", + "AttributeName": "worker_id" + }, + { + "KeyType": "RANGE", + "AttributeName": "message_id" + } + ], + "AttributeDefinitions": [ + { + "AttributeName": "worker_id", + "AttributeType": "S" + }, + { + "AttributeName": "message_id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST" +} +``` -In order for this driver to work, the database MUST have an outbox table with the following schema -(_called streams_egress by default_). +## Transactional Outbox Requirements +In order for this driver to work, the database MUST have an outbox table with the following schema. ```json { - "fields": {} + "TableName": "deduplication-table", + "KeySchema": [ + { + "KeyType": "HASH", + "AttributeName": "worker_id" + }, + { + "KeyType": "RANGE", + "AttributeName": "message_id" + } + ], + "AttributeDefinitions": [ + { + "AttributeName": "worker_id", + "AttributeType": "S" + }, + { + "AttributeName": "message_id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST" } ``` diff --git a/driver/dynamodb/deduplication_storage.go b/driver/dynamodb/deduplication_storage.go new file mode 100644 index 0000000..a3d4c84 --- /dev/null +++ b/driver/dynamodb/deduplication_storage.go @@ -0,0 +1,102 @@ +package dynamodb + +import ( + "context" + "log" + "os" + + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + + "github.com/aws/aws-sdk-go-v2/aws" + + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + + "github.com/alexandria-oss/streams" +) + +// DeduplicationStorageConfig is the configuration schema for Amazon DynamoDB streams.DeduplicationStorage implementation. +type DeduplicationStorageConfig struct { + TableName string + Logger *log.Logger + ErrorLogger *log.Logger +} + +// DeduplicationStorage is the Amazon DynamoDB streams.DeduplicationStorage +type DeduplicationStorage struct { + client *dynamodb.Client + cfg DeduplicationStorageConfig + tableRef *string +} + +var _ streams.DeduplicationStorage = DeduplicationStorage{} + +func NewDeduplicationStorage(cfg DeduplicationStorageConfig, client *dynamodb.Client) DeduplicationStorage { + if cfg.Logger == nil || cfg.ErrorLogger == nil { + logger := log.New(os.Stdout, "streams.dynamodb: ", 0) + if cfg.Logger == nil { + cfg.Logger = logger + } + if cfg.ErrorLogger == nil { + cfg.ErrorLogger = logger + } + } + return DeduplicationStorage{ + client: client, + cfg: cfg, + tableRef: aws.String(cfg.TableName), + } +} + +func (d DeduplicationStorage) Commit(ctx context.Context, workerID, messageID string) { + _, err := d.client.PutItem(ctx, &dynamodb.PutItemInput{ + Item: map[string]types.AttributeValue{ + "message_id": &types.AttributeValueMemberS{ + Value: messageID, + }, + "worker_id": &types.AttributeValueMemberS{ + Value: workerID, + }, + }, + TableName: d.tableRef, + ConditionExpression: nil, + ConditionalOperator: "", + Expected: nil, + ExpressionAttributeNames: nil, + ExpressionAttributeValues: nil, + ReturnConsumedCapacity: "", + ReturnItemCollectionMetrics: "", + ReturnValues: "", + }) + if err != nil { + d.cfg.ErrorLogger.Printf("failed to commit message, error %s", err.Error()) + return + } + + d.cfg.Logger.Printf("committed message with id <%s> and worker id <%s>", workerID, messageID) +} + +func (d DeduplicationStorage) IsDuplicated(ctx context.Context, workerID, messageID string) (bool, error) { + out, err := d.client.GetItem(ctx, &dynamodb.GetItemInput{ + Key: map[string]types.AttributeValue{ + "message_id": &types.AttributeValueMemberS{ + Value: messageID, + }, + "worker_id": &types.AttributeValueMemberS{ + Value: workerID, + }, + }, + TableName: d.tableRef, + AttributesToGet: nil, + ConsistentRead: nil, + ExpressionAttributeNames: nil, + ProjectionExpression: nil, + ReturnConsumedCapacity: "", + }) + + if err != nil { + d.cfg.ErrorLogger.Printf("failed to get message commit, error %s", err.Error()) + return false, err + } + + return len(out.Item) >= 2, nil +} diff --git a/driver/dynamodb/deduplication_storage_test.go b/driver/dynamodb/deduplication_storage_test.go new file mode 100644 index 0000000..c219914 --- /dev/null +++ b/driver/dynamodb/deduplication_storage_test.go @@ -0,0 +1,115 @@ +//go:build integration + +package dynamodb_test + +import ( + "context" + "strings" + "testing" + + streamsdynamo "github.com/alexandria-oss/streams/driver/dynamodb" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/stretchr/testify/suite" +) + +type dedupeStorageSuit struct { + suite.Suite + client *dynamodb.Client + tableName string +} + +func TestDeduplicationStorage(t *testing.T) { + suite.Run(t, &dedupeStorageSuit{}) +} + +func (s *dedupeStorageSuit) SetupSuite() { + cfg, err := config.LoadDefaultConfig(context.Background(), + config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider("fake", "fake", "TOKEN")), + config.WithRegion("us-east-1"), + config.WithEndpointResolverWithOptions(aws.EndpointResolverWithOptionsFunc( + func(service, region string, options ...interface{}) (aws.Endpoint, error) { + return aws.Endpoint{ + URL: "http://localhost:8001", + HostnameImmutable: false, + PartitionID: "aws", + SigningName: "", + SigningRegion: "us-east-1", + SigningMethod: "", + Source: 0, + }, nil + })), + ) + s.Require().NoError(err) + s.client = dynamodb.NewFromConfig(cfg) + s.tableName = "deduplication-storage" + s.runMigrations() +} + +func (s *dedupeStorageSuit) runMigrations() { + _, err := s.client.CreateTable(context.TODO(), &dynamodb.CreateTableInput{ + AttributeDefinitions: []types.AttributeDefinition{ + { + AttributeName: aws.String("message_id"), + AttributeType: "S", + }, + { + AttributeName: aws.String("worker_id"), + AttributeType: "S", + }, + }, + KeySchema: []types.KeySchemaElement{ + { + AttributeName: aws.String("message_id"), + KeyType: "HASH", + }, + { + AttributeName: aws.String("worker_id"), + KeyType: "RANGE", + }, + }, + TableName: aws.String(s.tableName), + BillingMode: "", + GlobalSecondaryIndexes: nil, + LocalSecondaryIndexes: nil, + ProvisionedThroughput: &types.ProvisionedThroughput{ + ReadCapacityUnits: aws.Int64(1), + WriteCapacityUnits: aws.Int64(1), + }, + SSESpecification: nil, + StreamSpecification: nil, + TableClass: "", + Tags: nil, + }) + if err != nil && !strings.Contains(err.Error(), "ResourceInUseException") { + s.Fail(err.Error()) + } +} + +func (s *dedupeStorageSuit) TearDownSuite() { + _, err := s.client.DeleteTable(context.TODO(), &dynamodb.DeleteTableInput{ + TableName: aws.String(s.tableName), + }) + s.Assert().NoError(err) +} + +func (s *dedupeStorageSuit) TestStorage() { + dedupeStorage := streamsdynamo.NewDeduplicationStorage(streamsdynamo.DeduplicationStorageConfig{ + TableName: s.tableName, + Logger: nil, + ErrorLogger: nil, + }, s.client) + worker := "worker-0" + messageID := "123" + isDupe, err := dedupeStorage.IsDuplicated(context.TODO(), worker, messageID) + s.Assert().Nil(err) + s.Assert().False(isDupe) + + dedupeStorage.Commit(context.TODO(), worker, messageID) + isDupe, err = dedupeStorage.IsDuplicated(context.TODO(), worker, messageID) + s.Assert().NoError(err) + s.Assert().True(isDupe) +} diff --git a/driver/dynamodb/docker-compose.yml b/driver/dynamodb/docker-compose.yml new file mode 100644 index 0000000..daad34d --- /dev/null +++ b/driver/dynamodb/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3.8' +services: + dynamodb-local: + command: "-jar DynamoDBLocal.jar -sharedDb -optimizeDbBeforeStartup -dbPath ./data" + image: "amazon/dynamodb-local:latest" + container_name: dynamodb-local + ports: + - "8001:8000" + volumes: + - "./:/home/dynamodblocal/data" + working_dir: /home/dynamodblocal diff --git a/driver/dynamodb/go.mod b/driver/dynamodb/go.mod index 1f87aa3..d9a8837 100644 --- a/driver/dynamodb/go.mod +++ b/driver/dynamodb/go.mod @@ -1,3 +1,44 @@ module github.com/alexandria-oss/streams/driver/dynamodb go 1.18 + +replace github.com/alexandria-oss/streams => ../../ + +require ( + github.com/alexandria-oss/streams v0.0.1-alpha.7 + github.com/aws/aws-sdk-go-v2 v1.17.8 + github.com/aws/aws-sdk-go-v2/config v1.18.21 + github.com/aws/aws-sdk-go-v2/credentials v1.13.20 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.5 + github.com/stretchr/testify v1.8.2 +) + +require ( + github.com/allegro/bigcache/v3 v3.1.0 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.26 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.18.9 // indirect + github.com/aws/smithy-go v1.13.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/eapache/go-resiliency v1.3.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/segmentio/ksuid v1.0.4 // indirect + google.golang.org/protobuf v1.29.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/driver/dynamodb/go.sum b/driver/dynamodb/go.sum new file mode 100644 index 0000000..43dc55b --- /dev/null +++ b/driver/dynamodb/go.sum @@ -0,0 +1,88 @@ +github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk= +github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I= +github.com/aws/aws-sdk-go-v2 v1.17.8 h1:GMupCNNI7FARX27L7GjCJM8NgivWbRgpjNI/hOQjFS8= +github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2/config v1.18.21 h1:ENTXWKwE8b9YXgQCsruGLhvA9bhg+RqAsL9XEMEsa2c= +github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY= +github.com/aws/aws-sdk-go-v2/credentials v1.13.20 h1:oZCEFcrMppP/CNiS8myzv9JgOzq2s0d3v3MXYil/mxQ= +github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2 h1:jOzQAesnBFDmz93feqKnsTHsXrlwWORNZMFHMV+WLFU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32 h1:dpbVNUjczQ8Ae3QKHbpHBpfvaVkRdesxpTOe9pTouhU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26 h1:QH2kOS3Ht7x+u0gHCh06CXL/h6G8LQJFpZfFBYBNboo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33 h1:HbH1VjUgrCdLJ+4lnnuLI4iVNRvBbBELGaJ5f69ClA8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33/go.mod h1:zG2FcwjQarWaqXSCGpgcr3RSjZ6dHGguZSppUL0XR7Q= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.5 h1:22zOCZ3Xf5qL0bH/Bc/jSH6P6SRTDPQEj2yxk+8wIXA= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.5/go.mod h1:2XzQIYZ2VeZzxUnFIe0EpYIdkol6eEgs3vSAFjTLw4Q= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.26 h1:XsLNgECTon/ughUzILFbbeC953tTbXnJv4GQPUHm80A= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.26/go.mod h1:zSW1SZ9ZQQZlRfqur2sI2Mn/ptcDLi6mtlPaXIIw0IE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26 h1:uUt4XctZLhl9wBE1L8lobU3bVN8SNUP7T+olb0bWBO4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.8 h1:5cb3D6xb006bPTqEfCNaEA6PPEfBXxxy4NNeX/44kGk= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8 h1:NZaj0ngZMzsubWZbrEFSB4rgSQRbFq38Sd6KBxHuOIU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.9 h1:Qf1aWwnsNkyAoqDqmdM3nHwN78XQjec27LjM6b9vyfI= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ= +github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eapache/go-resiliency v1.3.0 h1:RRL0nge+cWGlxXbUzJ7yMcq6w2XBEr19dCN6HECGaT0= +github.com/eapache/go-resiliency v1.3.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= +github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.29.0 h1:44S3JjaKmLEE4YIkjzexaP+NzZsudE3Zin5Njn/pYX0= +google.golang.org/protobuf v1.29.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/driver/dynamodb/reader.go b/driver/dynamodb/reader.go deleted file mode 100644 index 2d12944..0000000 --- a/driver/dynamodb/reader.go +++ /dev/null @@ -1 +0,0 @@ -package sns diff --git a/driver/dynamodb/writer.go b/driver/dynamodb/writer.go deleted file mode 100644 index 2d12944..0000000 --- a/driver/dynamodb/writer.go +++ /dev/null @@ -1 +0,0 @@ -package sns