kubernetes-client / java

Official Java client library for kubernetes
http://kubernetes.io/
Apache License 2.0
3.46k stars 1.84k forks source link

Memory leak in Cache indices when item is deleted #3510

Open wuxudong opened 6 days ago

wuxudong commented 6 days ago

Describe the bug

private void deleteFromIndices(ApiType oldObj, String key) {
    for (Map.Entry<String, Function<ApiType, List<String>>> indexEntry : this.indexers.entrySet()) {
      Function<ApiType, List<String>> indexFunc = indexEntry.getValue();
      List<String> indexValues = indexFunc.apply(oldObj);
      if (CollectionUtils.isEmpty(indexValues)) {
        continue;
      }

      Map<String, Set<String>> index = this.indices.get(indexEntry.getKey());
      if (index == null) {
        continue;
      }
      for (String indexValue : indexValues) {
        Set<String> indexSet = index.get(indexValue);
        if (indexSet != null) {
          indexSet.remove(key);
        }
      }
    }
  }

indexSet should also be deleted if it is empty; otherwise, a lot of untouchable HashSets will cause memory leaks.

The client-go fix is https://github.com/kubernetes/kubernetes/issues/84959

A proper fix would be:

private void deleteFromIndices(ApiType oldObj, String key) {
    for (Map.Entry<String, Function<ApiType, List<String>>> indexEntry : this.indexers.entrySet()) {
      Function<ApiType, List<String>> indexFunc = indexEntry.getValue();
      List<String> indexValues = indexFunc.apply(oldObj);
      if (CollectionUtils.isEmpty(indexValues)) {
        continue;
      }

      Map<String, Set<String>> index = this.indices.get(indexEntry.getKey());
      if (index == null) {
        continue;
      }
      for (String indexValue : indexValues) {
        Set<String> indexSet = index.get(indexValue);
        if (indexSet != null) {
          indexSet.remove(key);
          if (indexSet.isEmpty()) {
              index.remove(indexValue);
          }
        }
      }
    }
  }

Client Version 15.0.1

Kubernetes Version 1.19.3

Java Version Java 8

To Reproduce

Memory grows higher after items are created and removed.

Expected behavior

no memory leak after item is removed.

KubeConfig If applicable, add a KubeConfig file with secrets redacted.

Server (please complete the following information):

Additional context Add any other context about the problem here.

wuxudong commented 6 days ago

Compare to client-go, in thread_safe_store.go

func (i *storeIndex) deleteKeyFromIndex(key, indexValue string, index Index) {
    set := index[indexValue]
    if set == nil {
        return
    }
    set.Delete(key)
    // If we don't delete the set when zero, indices with high cardinality
    // short lived resources can cause memory to increase over time from
    // unused empty sets. See `kubernetes/kubernetes/issues/84959`.
    if len(set) == 0 {
        delete(index, indexValue)
    }
}
brendandburns commented 6 days ago

Makes sense, feel free to send a PR if you'd like. Otherwise we'll get to it eventually.