Redux API Cache

Hi all,

During the new host details page implementations (host statuses specifically), I was observing that we don’t have a client-side caching mechanism for API requests.
This lack might cause redundant API calls (for example, when we switch between tabs), which means fetching the same data over and over again.

This is the main reason why I’ve created this addition to our API middleware which adds a client-side caching mechanism.

How it works

This addition adds a new option - expiresIn, this adds a timestamp for the corresponding API calls.
If an API call owns the expiresIn value, the client-side will prevent from dispatching another API call with the same key in the given window.

Example
dispatch(
  get({
    key: SPECIAL_KEY,
    url,
    expiresIn: 10000, // an API call with the same key won't be dispatched unless at least 10 seconds been passed
  })
);

I’d like to hear more opinions about this approach or any other suggestion.

Thanks!

2 Likes

I’m always very hesitant with caches. They’re a great source of bugs due to inconsistencies. I’d discourage people from using them unless they’re really needed.

In a normal application HTTP responses could provide an expires header but there’s a reason we don’t set this in our API: there’s no guarantee that the data really is valid for that period of time.

It can also mask other things. If our API is slow, we should first see if we can actually make our API faster. If it’s sending too much data or you need too many API calls, perhaps GraphQL is a better fit.

In short: I’m opposed to this.

1 Like

+1 I think we should definitely look at ways of handling this issue in the UI design rather than trying to cache redux calls.

For example, should clicking between internal tabs (like the host details tabs) reload the data? This could save some API calls.

@amirfefer maybe you could detail out the root problem you are trying to fix with the statuses a bit more?

I agree that this might cause inconsistencies, but there are some limits:

  • it is more like a short-term caching which prevents redundant API in a short-term window
  • This feature helps especially in debouncing, so for example clicking a button multiple times in a second performs only one call.
  • It’s not by default - consumers need to add it for a reason

For example, should clicking between internal tabs (like the host details tabs) reload the data? This could save some API calls.
@John_Mitsch

In some cases, it makes sense to reload the data when switching between tabs (for keeping the data up-to-date) especially on an overview page which summarizes lots of data in one place, of course, there are other approaches, like pulling the data, but this approach basically prevents from dispatching multiple events in a given window, rather than keep the old data on our redux’s store.

I can’t imagine where we need this. Do you have an example?

I think the API layer is the wrong place for this. IMHO the UI must prevent the action from happening in the first place. You can’t “cache” a POST action so if a submit button is pressed twice, a cache is always the wrong thing to do. If you’re already solving it on another layer, why do you need caching in the API?

foreman at the moment doesn’t have much client-side rendering, so meanwhile, this feature might look redundant by itself however, during the implementation of the new host details page, I noticed that redundant API calls occur often (i.e switching between tabs for reloading content, modals reloading, etc).

I think the API layer is the wrong place for this

This is not the API layer, but our redux middleware layer, this addition prevents from triggering multiple redux actions in a given window, so in fact, it prevents the creation of the action that responsible the API call.

Redux caching is a known practice, I took some inspiration from this repo.
I understand this might cause issues, but also with a responsible usage and limitation (i.e max window value) we can benefit from this in the future.

I don’t necessary understand our new JS infrastructure, however one thing I understand pretty clearly: I try to stay away from caching as long as I can and only use this when there is no other way. There are many IT jokes about caching, point being, it’s a mess when things go wrong.

Is there any way that we can make things faster?

Can we utilize other type of cache like Rails ActiveRecord cache or even Rails Cache to cache all host statuses explicitly?

We ditched my dashboard cache PR patch and while I think that the dashboard is probably a good candidate for caching, we decided that it is not worth the effort. Not because of possible bugs, but mostly because irrelevant data being presented on screen.

1 Like

Before more replies about how caching can generally cause problems, I think it would be good to step back a bit and talk more about what problem we’re trying to solve. It may need more context so everyone understands and starts on the same page. @amirfefer could you please describe a few examples of how our UI can trigger two same requests withing a short window e.g. 1-5 seconds? Then we can talk about suggested solution advantages or disadvantages or suggest alternatives.

1 Like