Skip to content

Commit 24ab6eb

Browse files
committed
Fallback to approx create time check to catch replicated ttl removes.
1 parent 4d8132f commit 24ab6eb

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

src/from/dynamodb.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,23 @@ export const outGlobalTableExtraModify = (record) => {
165165
//--------------------------------------------
166166

167167
export const outTtlExpiredEvents = (ignoreTtlExpiredEvents) => (record) => {
168-
const { eventName, userIdentity } = record;
168+
const { eventName, userIdentity, dynamodb: { OldImage, ApproximateCreationDateTime } } = record;
169169
// this is not a REMOVE event or we're not ignoring the ttl expired events anyway.
170170
if (eventName !== 'REMOVE' || !ignoreTtlExpiredEvents) return true;
171171

172-
// See https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_Record.html
173-
// We trust dynamodb that the ttl expired if its a remove and has the ttl expiry indicating
174-
// identity attributes.
175-
return !(userIdentity?.type === 'Service' && userIdentity?.principalId === 'dynamodb.amazonaws.com');
172+
if (userIdentity) {
173+
// See https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_Record.html
174+
// We trust dynamodb that the ttl expired if its a remove and has the ttl expiry indicating
175+
// identity attributes.
176+
return !(userIdentity?.type === 'Service' && userIdentity?.principalId === 'dynamodb.amazonaws.com');
177+
} else if (OldImage.ttl?.N) {
178+
// If no user identity attribute is present, this may be a replicated TTL delete, but we still
179+
// want to honor it because filtering out replica region events may be disabled.
180+
const ttlSec = Number(OldImage.ttl.N);
181+
return !(ttlSec <= ApproximateCreationDateTime);
182+
} else {
183+
return true;
184+
}
176185
};
177186

178187
// test helper
@@ -185,6 +194,9 @@ export const toDynamodbRecords = (events, { removeUndefinedValues = true } = {})
185194
eventSource: 'aws:dynamodb',
186195
awsRegion: e.newImage?.awsregion || process.env.AWS_REGION || /* istanbul ignore next */ 'us-west-2',
187196
dynamodb: {
197+
// TODO - Fix this in next major version bump. ApproximateCreationDateTime is meant to be in seconds, not millis.
198+
// Didn't want to fix until a major version bump to avoid compatibility issues for consumers
199+
// upgrading the lib. This should be e.timestamp / 1000
188200
ApproximateCreationDateTime: e.timestamp,
189201
Keys: e.keys ? marshall(e.keys, { removeUndefinedValues }) : /* istanbul ignore next */ undefined,
190202
NewImage: e.newImage ? marshall(e.newImage, { removeUndefinedValues }) : undefined,

test/unit/from/dynamodb.test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,47 @@ describe('from/dynamodb.js', () => {
946946
.done(done);
947947
});
948948

949+
it('should ignore replicated ttl', (done) => {
950+
const events = toDynamodbRecords([
951+
{
952+
timestamp: 1573005491,
953+
keys: {
954+
pk: '1',
955+
sk: 'thing',
956+
},
957+
oldImage: {
958+
pk: '1',
959+
sk: 'thing',
960+
name: 'N1',
961+
ttl: 1573005490,
962+
timestamp: 1573005490000,
963+
},
964+
},
965+
{
966+
timestamp: 1573005490,
967+
keys: {
968+
pk: '1',
969+
sk: 'thing',
970+
},
971+
oldImage: {
972+
pk: '1',
973+
sk: 'thing',
974+
name: 'N1',
975+
ttl: 1573015490, // expired, has no identity attributes to indicate ttl delete
976+
timestamp: 1573005490000,
977+
},
978+
},
979+
]);
980+
981+
fromDynamodb(events, { ignoreTtlExpiredEvents: true })
982+
.collect()
983+
.tap((collected) => {
984+
// console.log(JSON.stringify(collected, null, 2));
985+
expect(collected.length).to.equal(1);
986+
})
987+
.done(done);
988+
});
989+
949990
it('should keep replica records if ignoreReplicas is false', (done) => {
950991
const events = toDynamodbRecords([
951992
{

0 commit comments

Comments
 (0)