Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion libs/common/include/launchdarkly/config/shared/built/events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ class Events final {
* should be made.
* @param flush_workers How many workers to use for concurrent event
* delivery.
* @param context_keys_cache_capacity Max number of unique context keys to
* hold in LRU cache used for context deduplication when generating index
* events.
*/
Events(bool enabled,
std::size_t capacity,
Expand All @@ -44,7 +47,8 @@ class Events final {
bool all_attributes_private,
AttributeReference::SetType private_attrs,
std::chrono::milliseconds delivery_retry_delay,
std::size_t flush_workers);
std::size_t flush_workers,
std::optional<std::size_t> context_keys_cache_capacity);

/**
* Returns true if event-sending is enabled.
Expand Down Expand Up @@ -87,6 +91,13 @@ class Events final {
*/
[[nodiscard]] std::size_t FlushWorkers() const;

/**
* Max number of unique context keys to hold in LRU cache used for context
* deduplication when generating index events.
* @return Max, or std::nullopt if not applicable.
*/
[[nodiscard]] std::optional<std::size_t> ContextKeysCacheCapacity() const;

private:
bool enabled_;
std::size_t capacity_;
Expand All @@ -96,6 +107,7 @@ class Events final {
AttributeReference::SetType private_attributes_;
std::chrono::milliseconds delivery_retry_delay_;
std::size_t flush_workers_;
std::optional<std::size_t> context_keys_cache_capacity_;
};

bool operator==(Events const& lhs, Events const& rhs);
Expand Down
6 changes: 4 additions & 2 deletions libs/common/include/launchdarkly/config/shared/defaults.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ struct Defaults<ClientSDK> {
false,
AttributeReference::SetType(),
std::chrono::seconds(1),
5};
5,
std::nullopt};
}

static auto HttpProperties() -> shared::built::HttpProperties {
Expand Down Expand Up @@ -88,7 +89,8 @@ struct Defaults<ServerSDK> {
false,
AttributeReference::SetType(),
std::chrono::seconds(1),
5};
5,
1000};
}

static auto HttpProperties() -> shared::built::HttpProperties {
Expand Down
13 changes: 10 additions & 3 deletions libs/common/src/config/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ Events::Events(bool enabled,
bool all_attributes_private,
AttributeReference::SetType private_attrs,
std::chrono::milliseconds delivery_retry_delay,
std::size_t flush_workers)
std::size_t flush_workers,
std::optional<std::size_t> context_keys_cache_capacity)
: enabled_(enabled),
capacity_(capacity),
flush_interval_(flush_interval),
path_(std::move(path)),
all_attributes_private_(all_attributes_private),
private_attributes_(std::move(private_attrs)),
delivery_retry_delay_(delivery_retry_delay),
flush_workers_(flush_workers) {}
flush_workers_(flush_workers),
context_keys_cache_capacity_(context_keys_cache_capacity) {}

bool Events::Enabled() const {
return enabled_;
Expand Down Expand Up @@ -51,13 +53,18 @@ std::size_t Events::FlushWorkers() const {
return flush_workers_;
}

std::optional<std::size_t> Events::ContextKeysCacheCapacity() const {
return context_keys_cache_capacity_;
}

bool operator==(Events const& lhs, Events const& rhs) {
return lhs.Path() == rhs.Path() &&
lhs.FlushInterval() == rhs.FlushInterval() &&
lhs.Capacity() == rhs.Capacity() &&
lhs.AllAttributesPrivate() == rhs.AllAttributesPrivate() &&
lhs.PrivateAttributes() == rhs.PrivateAttributes() &&
lhs.DeliveryRetryDelay() == rhs.DeliveryRetryDelay() &&
lhs.FlushWorkers() == rhs.FlushWorkers();
lhs.FlushWorkers() == rhs.FlushWorkers() &&
lhs.ContextKeysCacheCapacity() == rhs.ContextKeysCacheCapacity();
}
} // namespace launchdarkly::config::shared::built
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <launchdarkly/config/shared/built/events.hpp>
#include <launchdarkly/config/shared/built/service_endpoints.hpp>
#include <launchdarkly/context_filter.hpp>
#include <launchdarkly/events/lru_cache.hpp>
#include <launchdarkly/logging/logger.hpp>
#include <launchdarkly/network/http_requester.hpp>

Expand Down Expand Up @@ -74,6 +75,8 @@ class AsioEventProcessor {

launchdarkly::ContextFilter filter_;

LRUCache context_key_cache_;

Logger& logger_;

void HandleSend(InputEvent event);
Expand Down
3 changes: 3 additions & 0 deletions libs/internal/include/launchdarkly/events/events.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#pragma once

#include "client_events.hpp"
#include "server_events.hpp"

namespace launchdarkly::events {

using InputEvent = std::variant<client::FeatureEventParams,
Expand All @@ -10,6 +12,7 @@ using InputEvent = std::variant<client::FeatureEventParams,
using OutputEvent = std::variant<client::FeatureEvent,
client::DebugEvent,
client::IdentifyEvent,
server::IndexEvent,
TrackEvent>;

} // namespace launchdarkly::events
41 changes: 41 additions & 0 deletions libs/internal/include/launchdarkly/events/lru_cache.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once
#include <list>
#include <string>
#include <unordered_map>
namespace launchdarkly::events {

class LRUCache {
public:
/**
* Constructs a new cache with a given capacity. When capacity is exceeded,
* entries are evicted from the cache in LRU order.
* @param capacity
*/
explicit LRUCache(std::size_t capacity);

/**
* Adds a value to the cache; returns true if it was already there.
* @param value Value to add.
* @return True if the value was already in the cache.
*/
bool Notice(std::string const& value);

/**
* Returns the current size of the cache.
* @return Number of unique entries in cache.
*/
std::size_t Size() const;

/**
* Clears all cache entries.
*/
void Clear();

private:
using KeyList = std::list<std::string>;
std::size_t capacity_;
std::unordered_map<std::string, KeyList::reference> map_;
KeyList list_;
};

} // namespace launchdarkly::events
12 changes: 12 additions & 0 deletions libs/internal/include/launchdarkly/events/server_events.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "common_events.hpp"

namespace launchdarkly::events::server {

struct IndexEvent {
Date creation_date;
EventContext context;
};

} // namespace launchdarkly::events::server
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ void tag_invoke(boost::json::value_from_tag const&,
DebugEvent const& event);
} // namespace launchdarkly::events::client

namespace launchdarkly::events::server {

void tag_invoke(boost::json::value_from_tag const&,
boost::json::value& json_value,
IndexEvent const& event);
} // namespace launchdarkly::events::server

namespace launchdarkly::events {

void tag_invoke(boost::json::value_from_tag const&,
Expand Down
1 change: 1 addition & 0 deletions libs/internal/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ add_library(${LIBNAME} OBJECT
events/request_worker.cpp
events/summarizer.cpp
events/worker_pool.cpp
events/lru_cache.cpp
logging/console_backend.cpp
logging/null_logger.cpp
logging/logger.cpp
Expand Down
101 changes: 60 additions & 41 deletions libs/internal/src/events/asio_event_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <launchdarkly/network/asio_requester.hpp>
#include <launchdarkly/serialization/events/json_events.hpp>

#include <launchdarkly/events/server_events.hpp>

namespace http = boost::beast::http;
namespace launchdarkly::events {

Expand Down Expand Up @@ -53,6 +55,7 @@ AsioEventProcessor<SDK>::AsioEventProcessor(
last_known_past_time_(std::nullopt),
filter_(events_config.AllAttributesPrivate(),
events_config.PrivateAttributes()),
context_key_cache_(events_config.ContextKeysCacheCapacity().value_or(0)),
logger_(logger) {
ScheduleFlush();
}
Expand Down Expand Up @@ -212,47 +215,63 @@ std::vector<OutputEvent> AsioEventProcessor<SDK>::Process(
InputEvent input_event) {
std::vector<OutputEvent> out;
std::visit(
overloaded{[&](client::FeatureEventParams&& event) {
summarizer_.Update(event);

client::FeatureEventBase base{event};

auto debug_until_date = event.debug_events_until_date;

// To be conservative, use as the current time the
// maximum of the actual current time and the server's
// time. This way if the local host is running behind, we
// won't accidentally keep emitting events.

auto conservative_now = std::max(
std::chrono::system_clock::now(),
last_known_past_time_.value_or(
std::chrono::system_clock::from_time_t(0)));

bool emit_debug_event =
debug_until_date &&
conservative_now < debug_until_date->t;

if (emit_debug_event) {
out.emplace_back(client::DebugEvent{
base, filter_.filter(event.context)});
}

if (event.require_full_event) {
out.emplace_back(client::FeatureEvent{
std::move(base), event.context.KindsToKeys()});
}
},
[&](client::IdentifyEventParams&& event) {
// Contexts should already have been checked for
// validity by this point.
assert(event.context.Valid());
out.emplace_back(client::IdentifyEvent{
event.creation_date, filter_.filter(event.context)});
},
[&](TrackEventParams&& event) {
out.emplace_back(std::move(event));
}},
overloaded{
[&](client::FeatureEventParams&& event) {
summarizer_.Update(event);

if constexpr (std::is_same<SDK,
config::shared::ServerSDK>::value) {
if (!context_key_cache_.Notice(
event.context.CanonicalKey())) {
out.emplace_back(
server::IndexEvent{event.creation_date,
filter_.filter(event.context)});
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the actual change (1/2), not sure why the diff chunk is so large.


client::FeatureEventBase base{event};

auto debug_until_date = event.debug_events_until_date;

// To be conservative, use as the current time the
// maximum of the actual current time and the server's
// time. This way if the local host is running behind, we
// won't accidentally keep emitting events.

auto conservative_now =
std::max(std::chrono::system_clock::now(),
last_known_past_time_.value_or(
std::chrono::system_clock::from_time_t(0)));

bool emit_debug_event =
debug_until_date && conservative_now < debug_until_date->t;

if (emit_debug_event) {
out.emplace_back(client::DebugEvent{
base, filter_.filter(event.context)});
}

if (event.require_full_event) {
out.emplace_back(client::FeatureEvent{
std::move(base), event.context.KindsToKeys()});
}
},
[&](client::IdentifyEventParams&& event) {
// Contexts should already have been checked for
// validity by this point.
assert(event.context.Valid());

if constexpr (std::is_same<SDK,
config::shared::ServerSDK>::value) {
context_key_cache_.Notice(event.context.CanonicalKey());
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other change (2/2).


out.emplace_back(client::IdentifyEvent{
event.creation_date, filter_.filter(event.context)});
},
[&](TrackEventParams&& event) {
out.emplace_back(std::move(event));
}},
std::move(input_event));

return out;
Expand Down
32 changes: 32 additions & 0 deletions libs/internal/src/events/lru_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include <launchdarkly/events/lru_cache.hpp>

namespace launchdarkly::events {
LRUCache::LRUCache(std::size_t capacity)
: capacity_(capacity), map_(), list_() {}

bool LRUCache::Notice(std::string const& value) {
auto it = map_.find(value);
if (it != map_.end()) {
list_.remove(value);
list_.push_front(value);
return true;
}
while (map_.size() >= capacity_) {
map_.erase(list_.back());
list_.pop_back();
}
list_.push_front(value);
map_.emplace(value, list_.front());
return false;
}

void LRUCache::Clear() {
map_.clear();
list_.clear();
}

std::size_t LRUCache::Size() const {
return list_.size();
}

} // namespace launchdarkly::events
12 changes: 12 additions & 0 deletions libs/internal/src/serialization/events/json_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ void tag_invoke(boost::json::value_from_tag const& tag,
}
} // namespace launchdarkly::events::client

namespace launchdarkly::events::server {

void tag_invoke(boost::json::value_from_tag const&,
boost::json::value& json_value,
IndexEvent const& event) {
auto& obj = json_value.emplace_object();
obj.emplace("kind", "index");
obj.emplace("creationDate", boost::json::value_from(event.creation_date));
obj.emplace("context", event.context);
}
} // namespace launchdarkly::events::server

namespace launchdarkly::events {

void tag_invoke(boost::json::value_from_tag const& tag,
Expand Down
Loading