typeorm: AfterUpdate subscriber bug

Issue type:

[ ] question [x] bug report [ ] feature request [ ] documentation issue

Database system/driver:

[ ] cordova [ ] mongodb [ ] mssql [ ] mysql / mariadb [ ] oracle [x] postgres [ ] sqlite [ ] sqljs [ ] websql

TypeORM version:

[ ] latest [ ] @next [x] 0.1.9 (or put your version here)

Steps to reproduce or a small repository showing the problem:

  1. Create entity.
  2. Add method with @AfterUpdate decorator to it.
  3. Update entity (with getRepository(Entity).save()).

Expected behaviour: When method with @AfterUpdate runs, I can get updated entity from database.

Actual behaviour: Here’s what actually happens:

  1. Transaction is created (executing query: START TRANSACTION)
  2. UPDATE query is executed (executing query: UPDATE "entity" SET "something"=$1 WHERE "id"=$2 -- PARAMETERS: [1,2])
  3. Method with @AfterUpdate is called.
  4. Transaction is commited.

So, when @AfterUpdate method is called, entity in the database has not been updated yet. Therefore, I can not get updated entities from the database.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 20 (5 by maintainers)

Most upvoted comments

I think this behaviour should be documented. Coming from a framework like Rails, the callbacks happen after the transaction has been committed.

I don’t understand why this has been closed. I believe this is still a needed feature. As I see it, the two behaviors should be supported:

  • Listeners/Subscribers being called before the transaction is committed (current behavior)
  • Listeners/Subscribers being called after the transaction is committed. I can’t think of a better example as what @didac-wh shown. Sync’ing entries on different database engines (like elasticsearch) must be done after transactions are committed. Otherwise, if the transaction fails for any reason, both databases won’t be sync’ed anymore.

Is there any plan to support this in the future? I see it was requested at #743 too? are PR implementing this welcome?

@didac-wh, did overwriting save method work when the transaction did fail? I mean, I would expect this.updateElasticSearch() not being called in your example.

@pleerock Wouldn’t it be more convenient to introduce a afterUpdateCommit hook? There might be some rare use-case (I think I stumbled into one of them), where the above solutions wouldn’t work.

I don’t get it. I’m using a subscriber, and as shown in the image: https://i.imgur.com/sYmjVCK.png “Called after entity is updated in the database”

Well, in my ElasticSearch update function I use a parent of this entity, and it updates that parent (and some relations, including my current entity) but the values I get in that process are the old ones. Even if I call entity.reload(). Even if I call “await repository.findOne(entity.id)” to re-assign the entity value.

My thinking is that it’s not only undocumented in the docs, but its also missdocumented according to the TypeScript definition.

For a solution, I ended up overriding the save() method for my entity.

async save() : Promise<this> {
    await super.save();
    await this.updateElasticSearch();

    return this;
  }

It seems to work so far.

I’m not sure if this is the best solution, but for anyone else who comes across this issue, this is what fixed it for us. We just added the following lines at the beginning of the afterUpdate function:

    await event.queryRunner.commitTransaction();
    await event.queryRunner.startTransaction();

In our case, the issue was that we were sending IDs to a queue to trigger search indexing, and the queue was being processed before the transaction was committed (though I found that surprising). So data in our search index was always one step behind the actual data.

For most use cases I can imagine, I think the naming of the subscriber is misleading. To me, afterUpdate implies that it will be called after everything to do with the update is complete. Of course at this point, if people rely on this behaviour it’s probably not changeable, but I would have expected something like updatePreCommit or something like that for the current behaviour.

This issue still exists. It would be nice to have an event that emit after the transaction is committed to cater to rest of use cases as well

any updated about this ? would be it possible to have the same InsertEvent or UpdateEvent object passed to callback decorated with @AfterUpdate and so on ?

@cjuega have you tried to use a subscriber as described here: https://github.com/typeorm/typeorm/issues/3563#issuecomment-460054971 ?