Chasing a Bug when Working with an RxJS Observable

Georg Dangl by Georg Dangl in Web Development Thursday, December 21, 2017

Thursday, December 21, 2017

Posted in TypeScript

I'm the author of LightQuery, a library that offers simple pagination and sorting functionalities for Asp.Net Core web services. I've also created client libraries to consume these services, both for .Net and TypeScript with Angular. It's useful for these ever-repeating tables of data that you find in almost any app and integrates nicely with popular table and pagination modules, such as from Angular Material or ngx-bootstrap.

Both client apps provide a generic implementation of PaginationBaseService<T>, which handle all the sorting and paging of list resources. In one of my apps, I've extended this service with methods like getElementById(id: string). They make use of the service caching the last retrieved pagination result from the server. If the requested item is locally available, I can directly return a resolved Promise instead of requerying over http. This is as simple as in the following snippet:

There's a detail view component as part of the app. You select an item from a list or table, then you can view it in detail. If desired, you can toggle an editor mode to modify the record. CRUD 101, you've written that yourself often enough. The detail component watches the editor. If it's being informed that the entity was updated, it forces the pagination service to update it's current record set and then follow on with a retrieval of the item by its id.

Now that implementation is not very efficient. When I do update a single record, it doesn't use the actual response from the server but just requeries the whole list again. But it worked as a first iteration. Or did it?

The snippet above is from the service implementation. It's of interest that the local cache variable lastPaginationResult is only updated after the Observable has emitted the latest result. And that's the catch. I didn't pay attention to the order of execution for subscribed actions to Observables, but I should have. As soon as an Observable emits a value, it's subscriber actions are invoked. Meaning that there's quite a lot of stuff happening between lines 12 and 13. Looking back at the subscribe() in the main component: It's querying for a single element as soon as the paginationResult is updated. Since an element with this id is still in the (stale!) cache, there's no asynchronous actions being invoked. The execution happens directly in a single chain, deterministic without interruption. And before updating the services local cache. I've since created a unit test to catch that behavior, and the fix was as easy as simply changing the order of two statements in my code.

This was a lucky catch for me, the bug was subtle but interesting, easy to diagnose and fix, yet it alerted me to pay closer attention when working with reactive patterns. The service was later refactored to use the server response to the PUT request to directly refresh its cache and list without additional network roundtrips. If I had done that right from the start, I wouldn't have caught this subtle bug. And I would also have continued to use reactive patterns wrong.

Happy Debugging and Merry Christmas!

Share this post

comments powered by Disqus

// Just 💗 Coding

Social Links