1
#include "source/extensions/config_subscription/grpc/eds_resources_cache_impl.h"
2

            
3
#include <algorithm>
4

            
5
#include "source/common/common/logger.h"
6

            
7
namespace Envoy {
8
namespace Config {
9

            
10
void EdsResourcesCacheImpl::setResource(
11
    absl::string_view resource_name,
12
382
    const envoy::config::endpoint::v3::ClusterLoadAssignment& resource) {
13
382
  resources_map_.insert_or_assign(resource_name, ResourceData(resource));
14
382
}
15

            
16
354
void EdsResourcesCacheImpl::removeResource(absl::string_view resource_name) {
17
354
  if (const auto& resource_it = resources_map_.find(resource_name);
18
354
      resource_it != resources_map_.end()) {
19
    // Invoke the callbacks, and remove all watchers.
20
320
    for (auto& removal_cb : resource_it->second.removal_cbs_) {
21
4
      removal_cb->onCachedResourceRemoved(resource_name);
22
4
    }
23
320
    resource_it->second.removal_cbs_.clear();
24

            
25
    // Remove the resource entry from the cache.
26
320
    resources_map_.erase(resource_it);
27
320
  }
28
  // Remove the expiration timers (if any) as there's no longer interest in the resource.
29
354
  expiry_timers_.erase(resource_name);
30
354
}
31

            
32
OptRef<const envoy::config::endpoint::v3::ClusterLoadAssignment>
33
EdsResourcesCacheImpl::getResource(absl::string_view resource_name,
34
31
                                   EdsResourceRemovalCallback* removal_cb) {
35
31
  if (const auto& resource_it = resources_map_.find(resource_name);
36
31
      resource_it != resources_map_.end()) {
37
28
    ENVOY_LOG_MISC(trace, "Returning resource {} from the xDS resource cache", resource_name);
38
    // Add the removal callback to the list associated with the resource.
39
28
    if (removal_cb != nullptr) {
40
25
      resource_it->second.removal_cbs_.push_back(removal_cb);
41
25
    }
42
28
    return resource_it->second.resource_;
43
28
  }
44
  // The resource doesn't exist in the resource map.
45
3
  return {};
46
31
}
47

            
48
void EdsResourcesCacheImpl::removeCallback(absl::string_view resource_name,
49
19
                                           EdsResourceRemovalCallback* removal_cb) {
50
19
  if (const auto& resource_it = resources_map_.find(resource_name);
51
19
      resource_it != resources_map_.end()) {
52
19
    ENVOY_LOG_MISC(trace, "Removing callback for resource {} from the xDS resource cache",
53
19
                   resource_name);
54
19
    auto& callbacks = resource_it->second.removal_cbs_;
55
    // Using the Erase-Remove idiom, in which all entries to be removed are
56
    // moved to the end of the vector by the remove() call, and then the erase() call
57
    // will erase these entries from the first element to be removed all the
58
    // way to the end of the vector.
59
19
    callbacks.erase(std::remove(callbacks.begin(), callbacks.end(), removal_cb), callbacks.end());
60
19
  }
61
19
}
62

            
63
30
uint32_t EdsResourcesCacheImpl::cacheSizeForTest() const { return resources_map_.size(); }
64

            
65
void EdsResourcesCacheImpl::setExpiryTimer(absl::string_view resource_name,
66
2
                                           std::chrono::milliseconds ms) {
67
2
  auto it = expiry_timers_.find(resource_name);
68
2
  if (it == expiry_timers_.end()) {
69
    // No timer for this resource, create one, and create a copy of resource_name that will outlive
70
    // this function.
71
2
    Event::TimerPtr resource_timeout =
72
2
        dispatcher_.createTimer([this, str_resource_name = std::string(resource_name)]() -> void {
73
          // On expiration the resource is removed (from the cache and from the watchers).
74
1
          removeResource(str_resource_name);
75
1
        });
76
2
    it = expiry_timers_.emplace(resource_name, std::move(resource_timeout)).first;
77
2
  }
78
2
  (it->second)->enableTimer(ms);
79
2
}
80

            
81
1
void EdsResourcesCacheImpl::disableExpiryTimer(absl::string_view resource_name) {
82
1
  auto it = expiry_timers_.find(resource_name);
83
1
  if (it != expiry_timers_.end()) {
84
1
    (it->second)->disableTimer();
85
    // Remove the timer as it is no longer needed.
86
1
    expiry_timers_.erase(it);
87
1
  }
88
1
}
89

            
90
} // namespace Config
91
} // namespace Envoy