Skip to content

Need write back cache #1622

@dee0sap

Description

@dee0sap

Ticket https://github.com/crossplane/crossplane/issues/2435 was opened a due to a leak of resources in AWS.
Multiple reasons for the leak were found. One of the reasons is a caching/concurrency problem in controller-runtime.

In the above ticket the caching/concurrency problem involved an AWS route table managed resource. Reconcile created an additional one in AWS because after it had set the external name it was asked to Reconcile again when the cache had an out of date instance of the route table, one where external-name hasn't been set yet. This happened despite the use of HasSynced in specificInformersMap::Get.

These comments to the ticket give the details of how this is happening
https://github.com/crossplane/crossplane/issues/2435#issuecomment-891318989
https://github.com/crossplane/crossplane/issues/2435#issuecomment-890462636
https://github.com/crossplane/crossplane/issues/2435#issuecomment-890424587

A couple of 'band-aid' fixes would be to

  • Get AWS to update their API so that the route table creation would allow the user to supply the unique id
  • Update ResolveReferences to return an indication of whether the resources was updated or not in addition to returning an error.

The first would only solve the problem for AWS route tables which isn't ideal.

The second would only solve the problem when the additional Reconcile, the one where an additional resource in AWS is created, is the result of reference resolution. However since updates to the managed resource object in the API server could come from anywhere this also isn't ideal.

A write-back cache would provide a complete solution. Here is an outline of the changes I think are required.

== client-go ==
These changes just make it so cache mutation continues to work as expected.

  • In shared_informer.go add indexer which embeds Indexer .
    Move cacheMutationDetector from sharedIndexInformer to indexer.
    indexer will wrap calls to Add, Update and Replace. The wrapper methods will forward the passed in object to cacheMutationDetector before the object to the appropriate method of the embedded Indexer.
    NOTE: This plugs a gap in the current cache mutation detection that exists today. Today detection only is performed on objects added to the cache via sharedIndexInformer::HandleDeltas.
  • In shared_informer.go remove cacheMutationDetector from sharedIndexInformer. Change construction of sharedIndexInformer to use indexer mentioned above.

== controller-runtime ==

  • Add CacheUpdater which is similar to CacheReader defined in cache_reader.go.
    It only has an Update method which forwards its parameters to indexer.Update.
  • Add Updater method to MapEntry.
  • Update addInformerToMap in so it sets Updater of MapEntry.
  • Add Updater interface to interfaces.go. This just has Update method identical to the one on the Writer interface.
  • Add Update method to informerCache. which will be similar Get etc.
  • Update interface Cache in cache.go so it embeds above Updater.
  • Add cacheWriteBAck struct similar to delegatingReader. It will embed Writer and it will have to the cache and the uncachedGVKs. It will forward calls to Writer first and if those succeed and, if uncachedGVKs doesn't indicate otherwise, call Update on the cache, passing the updated object from the server.
  • Update NewDelegatingClient so that constructs a delegatingClient with a cacheWriteThrough for the writer.

@hasheddan @negz

Metadata

Metadata

Assignees

No one assigned

    Labels

    lifecycle/rottenDenotes an issue or PR that has aged beyond stale and will be auto-closed.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions