Skip to content

Commit

Permalink
Support copying the most recent item to the counter table
Browse files Browse the repository at this point in the history
This is nasa-gcn#20 as a one-liner.
  • Loading branch information
lpsinger committed Oct 17, 2023
1 parent 013d232 commit 928349a
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 55 deletions.
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export interface DynamoDBAutoIncrementProps {
/** the name of the attribute in the table in which to store the last value of the counter */
counterTableAttributeName: string

/** whether to copy all of the attributes from the table to the counterTable */
counterTableCopyItem?: boolean

/** the name of the table in which to store items */
tableName: string

Expand Down Expand Up @@ -94,6 +97,7 @@ export class DynamoDBAutoIncrement {
},
ExpressionAttributeValues,
Item: {
...(this.props.counterTableCopyItem ? item : {}),
...this.props.counterTableKey,
[this.props.counterTableAttributeName]: nextCounter,
},
Expand Down
141 changes: 86 additions & 55 deletions src/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,66 +59,97 @@ afterEach(async () => {
)
})

describe('dynamoDBAutoIncrement', () => {
test.each([undefined, 1, 2, 3])(
'creates a new item with the correct ID when the old ID was %o',
async (lastID) => {
let nextID: number
if (lastID === undefined) {
nextID = 1
} else {
await doc.put({
TableName: 'autoincrement',
Item: { tableName: 'widgets', counter: lastID },
})
nextID = lastID + 1
describe.each([false, true])(
'counterTableCopyItem=%p',
(counterTableCopyItem) => {
beforeAll(async () => {
const options: DynamoDBAutoIncrementProps = {
doc,
counterTableName: 'autoincrement',
counterTableKey: { tableName: 'widgets' },
counterTableAttributeName: 'counter',
counterTableCopyItem,
tableName: 'widgets',
tableAttributeName: 'widgetID',
initialValue: 1,
}
autoincrement = new DynamoDBAutoIncrement(options)
autoincrementDangerously = new DynamoDBAutoIncrement({
...options,
dangerously: true,
})
})

const result = await autoincrement.put({ widgetName: 'runcible spoon' })
expect(result).toEqual(nextID)
describe('dynamoDBAutoIncrement', () => {
test.each([undefined, 1, 2, 3])(
'creates a new item with the correct ID when the old ID was %o',
async (lastID) => {
let nextID: number
if (lastID === undefined) {
nextID = 1
} else {
await doc.put({
TableName: 'autoincrement',
Item: { tableName: 'widgets', counter: lastID },
})
nextID = lastID + 1
}

expect(await autoincrement.getLast()).toEqual(nextID)
const result = await autoincrement.put({
widgetName: 'runcible spoon',
})
expect(result).toEqual(nextID)

const [widgetItems, autoincrementItems] = await Promise.all(
['widgets', 'autoincrement'].map(
async (TableName) => (await doc.scan({ TableName })).Items
)
)
expect(await autoincrement.getLast()).toEqual(nextID)

expect(widgetItems).toEqual([
{ widgetID: nextID, widgetName: 'runcible spoon' },
])
expect(autoincrementItems).toEqual([
{
tableName: 'widgets',
counter: nextID,
},
])
}
)
const [widgetItems, autoincrementItems] = await Promise.all(
['widgets', 'autoincrement'].map(
async (TableName) => (await doc.scan({ TableName })).Items
)
)

test('correctly handles a large number of parallel puts', async () => {
const ids = Array.from(Array(N).keys()).map((i) => i + 1)
const result = await Promise.all(ids.map(() => autoincrement.put({})))
expect(result.sort()).toEqual(ids.sort())
})
})
expect(widgetItems).toEqual([
{ widgetID: nextID, widgetName: 'runcible spoon' },
])
expect(autoincrementItems).toEqual([
counterTableCopyItem
? {
tableName: 'widgets',
counter: nextID,
widgetName: 'runcible spoon',
}
: {
tableName: 'widgets',
counter: nextID,
},
])
}
)

describe('dynamoDBAutoIncrement dangerously', () => {
test('correctly handles a large number of serial puts', async () => {
const ids = Array.from(Array(N).keys()).map((i) => i + 1)
const result: number[] = []
for (const item of ids) {
result.push(await autoincrementDangerously.put({ widgetName: item }))
}
expect(result.sort()).toEqual(ids.sort())
})
test('correctly handles a large number of parallel puts', async () => {
const ids = Array.from(Array(N).keys()).map((i) => i + 1)
const result = await Promise.all(ids.map(() => autoincrement.put({})))
expect(result.sort()).toEqual(ids.sort())
})
})

test('fails on a large number of parallel puts', async () => {
const ids = Array.from(Array(N).keys()).map((i) => i + 1)
await expect(
async () =>
await Promise.all(ids.map(() => autoincrementDangerously.put({})))
).rejects.toThrow(ConditionalCheckFailedException)
})
})
describe('dynamoDBAutoIncrement dangerously', () => {
test('correctly handles a large number of serial puts', async () => {
const ids = Array.from(Array(N).keys()).map((i) => i + 1)
const result: number[] = []
for (const item of ids) {
result.push(await autoincrementDangerously.put({ widgetName: item }))
}
expect(result.sort()).toEqual(ids.sort())
})

test('fails on a large number of parallel puts', async () => {
const ids = Array.from(Array(N).keys()).map((i) => i + 1)
await expect(
async () =>
await Promise.all(ids.map(() => autoincrementDangerously.put({})))
).rejects.toThrow(ConditionalCheckFailedException)
})
})
}
)

0 comments on commit 928349a

Please sign in to comment.