[identity] Implement transactional device list updates
Summary:
One of the most important commits in this stack. Added code that allows us to handle transactional device list updates. Adding/removing devices should generate a new device list (and in the future sign it), while keeping previous ones in database, to have its full history.
The transaction goes as follows:
- Previous device list timestamp is fetched from database (user table)
- All user's current devices are fetched from database (device table)
- A caller-defined function is called to:
- Mutably modify the "current devices"
- Return a database update to be performed in transaction
- A new device list is generated from the modified "current devices" (with new timestamp)
- The database update is performed in transaction:
- Current devices are updated (closure-returned DDB operation is performed)
- New device list is inserted with new timestamp
- The timestamp in user table is updated to the new device list timestamp
- Previous timestamp is checked to be equal to the one fetched in step 1. Transaction fails on mismatch
Depends on D10218
Test Plan:
Having this closure:
transact_update_devicelist(db, user_id, |ref mut devices| { let new_device = DeviceRow { // omitted for clarity }; devices.push(new_device.clone()); Ok(TransactWriteItem::builder().put(Put::builder() .table_name("identity-devices") .set_item(Some(new_device.into())) .build() )) }).await `
Tested a few scenarios, including:
- Normal, the transaction succeeds
- Malfolmed DDB operation - the function returned Error::AwsSdk(err) with my mistake
- Added code that concurrently modifies the timestamp in the users table - transaction fails with DeviceListError::ConcurrentUpdate
Reviewers: michal, varun
Reviewed By: michal, varun
Subscribers: ashoat, tomek
Differential Revision: https://phab.comm.dev/D10219