Optimistic Locking doesn't work for update
See original GitHub issueIssue type:
[ ] question [ ] bug report [ ] feature request [ ] documentation issue
Database system/driver:
[ ] cordova
[ ] mongodb
[ ] mssql
[ ] mysql
/ mariadb
[ ] oracle
[x ] postgres
[ ] sqlite
[ ] sqljs
[ ] react-native
[ ] expo
TypeORM version:
[x ] latest
[ ] @next
[ ] 0.x.x
(or put your version here)
Steps to reproduce or a small repository showing the problem: Hi All,
Trying to use the @Version annotation to implement optimistic locking for ensuring entity updates don’t stomp on newer data. I couldn’t find an example using QueryRunner along with update(), but I tried it anyway like this:
await transactionalRepository.createQueryBuilder(“c”) .setLock(“optimistic”, caseEntity.version) .update() .set(this.createCaseUpdateObj(caseEntity)) .where(“id = :id”, {id: caseEntity.id}) .execute();
However, this operation succeeds no matter what I pass to setLock for version. I CAN ensure that I don’t stomp on newer data by augmenting my query like so:
await transactionalRepository.createQueryBuilder(“c”) .update() .set(this.createCaseUpdateObj(caseEntity)) .where(“id = :id”, {id: caseEntity.id}) .andWhere(“version = :version”, {version: caseEntity.version}) .execute();
However, the problem now is I have no way of finding out whether my update was actually applied. To do that, I need to know how many rows where updated by my query, which does not appear to be returned by this method.
Are there plans to make setLock() handle this case for update() (most ORMs support this)? If not, are there plans to return the number of rows updated by an operation like the one above? If so, any idea of timeframe?
I think until then I’d probably need to drop down to lower level postgres driver to safely perform an update operation.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:8 (1 by maintainers)
I see that issue 1308 would let me check to see how many rows were updated, which would allow me to safely implement an Optimistic Lock for update. This would be a fine intermediate result.
However, I think longer term actually making setLock() with update() work like this would be a more intuitive and reusable API, especially given this is how other ORMs behave.
I stumbled on a reliable way of making this work for Postgres:
//The version where clause below prevents an update from occurring if a newer version is already in the database //Now, we need to determine if the update did occur so we can signal the user, if not. //The traditional way to do this would be to check to see how many rows where updated - if it’s zero, the updated didn’t go through due to version //TypeORM doesn’t return the number of rows affected, but can use .returning().
//This takes an array of fields to return the updated values for.
//If the update doesn’t go through due to the version where clause, this array will be empty const result = await manager.createQueryBuilder() .update(entityName) .set(updateFields) .where(“id = :id”, {id: entity.id}) .andWhere(“version = :version”, {version: entity.version}) .returning(“version”) .execute();
As discussed in the comment above, using .returning() gives a reliable indicator of whether the update went through. Combining this with the version checking where clause makes an acceptable solution.
Having the .setLock() and @Version feature work together to accomplish this would be better, but this is OK for now.