Let's look at the snippet that will take care of invalidating the cached options.
For each update of a article content, we retrieve the cached options ($data), and then compare the year of the date of the content being saved with the cached values.
In case of absence of the year, thanks to the magic cache tags, then we simply need to invalidate the custom cache tag (node:article:year) that we have associated with the cached options.
And the next time you load the articles view, the options will be recalculated, updated with the new year, and re-cached for an indefinite period, and no doubt annually.
Then, to clear that entity's cache you simply call
Cache::invalidateTags(['article:news:year']);
Example
/**
* Implements hook_ENTITY_TYPE_presave().
*/
function mytheme_node_presave(EntityInterface $entity) {
$bundle = $entity->bundle();
if ($bundle == 'article') {
// Check if a article updated has a new year, and invalidate the
// options cached used in the custom views filter for filtering by year.
$cid = 'article:news:year';
$data = \Drupal::cache()->get($cid);
if ($data) {
$options = $data->data;
$date = $entity->created->value;
$date = date('Y-m-d H:i:s', $date);
if ($date) {
$date = new DrupalDateTime($date, new DateTimeZone('UTC'));
$year = $date->format('Y');
if (!isset($options[$year])) {
Cache::invalidateTags(['article:news:year']);
}
}
}
}
}