Implement caching for issue board lists polling
This is a sub-task of https://gitlab.com/gitlab-org/gitlab-ce/issues/22675 and the goal is to make polling of real-time board changes more performant.
Problem
For every issue board being displayed by user, frontend will poll periodically (every few seconds) for changes in issues being displayed in this board. Because issues will not change most of the time, it wouldn't be very effective to re-load all issues for the given board from DB on each poll request. We would rather want to use ETag caching (https://docs.gitlab.com/ee/development/polling.html) which we already use on other places.
Currently FE loads list of issues for each board list separately by requesting URL (project_or_group_namespace)/boards/:id/lists/:list_id/issues
.
We would like to implement ETag caching on the board list level (TODO: consider caching on board level so FE would do one polling request for the whole board). So we would need to invalidate ETag cache when an issues in the given board list changes. This is challenging because:
- when an existing issue is updated/deleted we don't know in which board lists it's being displayed so we don't know which possible ETag keys should be invalidated
- when a new issue is created we don't know in which lists it is displayed (https://gitlab.com/gitlab-org/gitlab-ce/issues/22675#note_105462776)
Possible solution
(based on Sean's idea we discussed on Slack)
We could use list of cache keys for keeping list of board lists on which an issue is displayed:
- we add 2 helper redis cache keys (both scoped in group namespace):
issue_board_lists_keys
- for keeping list of all cache keys, andissue_board_lists_$issue_id
- for keeping list of board lists on which given issue is displayed - user views a board list, that loads some issues
- each issue creates a cache key:
issue_board_lists_$issue_id
and adds this key to theissue_board_lists_keys
(which keeps list ofissue_board_lists_$issue_id
) - value of
issue_board_lists_$issue_id
is a set of board list IDs, if the key already exists, it adds the board list ID to that set - when an issue is updated, it checks if there is
issue_board_lists_$issue_id
and if so, it invalidates ETag cache for all board lists listed in the value of this key -
issue_board_lists_$issue_id
cache key would have limited TTL, but higher than TTL of ETag cache key (which is 20 minutes), we would also "renew" TTL on each update of the key -
issue_board_lists_keys
's TTL would be either unlimited or renewed together with update of any ofissue_board_lists_$issue_id
keys (TODO: double-check invalidation of this - we want to make sure this variable doesn't just keep growing) - when a new issue is created, we have to invalidate all board lists being polled on which this issue can be displayed. This is the reason why we keep
issue_board_lists_keys
- we get allissue_board_lists_$issue_id
keys from this cache key (because we can't easily get from redis all keys matchingissue_board_lists_$issue_id
pattern). And we invalidate ETag cache key for all board lists in allissue_board_lists_$issue_id
.
E.g. we display issues with ID 23
on board lists 11
and 12
, and issue with ID 24
on board lists 13
and 14
. Both issue belongs to group ID 2
. We would create following redis cache keys:
'/groups/2/issue_board_lists_keys': ['/groups/2/issue_board_lists_23', '/groups/2/issue_board_lists_24']
'/groups/2/issue_board_lists_23': [11, 12]
'/groups/2/issue_board_lists_24': [13, 14]
We could split this into 2 iterations:
First iteration
Add only issue_board_lists_$issue_id
cache keys. In this iteration we wouldn't invalidate cache keys when a new issue is added.
Second iteration
Add issue_board_lists_keys
which keeps list of existing issue_board_lists_$issue_id
keys and invalidate them when new issue is added.