Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FilterUtility::GetFilterTargets(): don't run filter for specific object(s) for all objects #9895

Merged
merged 6 commits into from
Dec 19, 2023

Conversation

Al2Klimov
Copy link
Member

This is basically the same as

but for API filters, not for assign rules:

  1. There's a filter on Hosts/Services
  2. Does it unambiguously target only specific Hosts/Services?
  3. If yes: fetch just them, don't run filter for all

@Al2Klimov Al2Klimov added enhancement New feature or request area/api REST API consider backporting Should be considered for inclusion in a bugfix release labels Nov 8, 2023
@Al2Klimov Al2Klimov added this to the 2.15.0 milestone Nov 8, 2023
@cla-bot cla-bot bot added the cla/signed label Nov 8, 2023
@icinga-probot icinga-probot bot added area/configuration DSL, parser, compiler, error handling core/quality Improve code, libraries, algorithms, inline docs ref/IP labels Nov 8, 2023
Comment on lines +278 to +286
if (dynamic_cast<ConfigObjectTargetProvider*>(provider.get())) {
auto dict (dynamic_cast<DictExpression*>(ufilter.get()));

if (dict) {
auto& subex (dict->GetExpressions());

if (subex.size() == 1u) {
if (type == "Host") {
Copy link
Member Author

Choose a reason for hiding this comment

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

  1. There's a filter on Hosts/Services

if (type == "Host") {
std::vector<const String *> targetNames;

if (GetTargetHosts(subex.at(0).get(), filter_vars, targetNames)) {
Copy link
Member Author

Choose a reason for hiding this comment

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

  1. Does it unambiguously target only specific Hosts/Services?

Comment on lines +322 to +328
if (targeted) {
for (auto& target : targets) {
if (FilterUtility::EvaluateFilter(permissionFrame, permissionFilter.get(), target, variableName)) {
result.emplace_back(std::move(target));
}
}
Copy link
Member Author

Choose a reason for hiding this comment

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

  1. If yes: fetch just them, don't run filter for all

@Al2Klimov
Copy link
Member Author

Test

$ time curl -ksSu root:123456 -o /dev/null -H 'Accept: application/json' -X GET -d '{"filter":"y == service.name && x == host.name","filter_vars":{"x":"alexandersmbp2.int.netways.de","y":"42"}}' 'https://127.0.0.1:5665/v1/objects/services'
curl -ksSu root:123456 -o /dev/null -H 'Accept: application/json' -X GET -d    0,01s user 0,01s system 22% cpu 0,058 total
$ time curl -ksSu root:123456 -o /dev/null -H 'Accept: application/json' -X GET -d '{"filter":"y == service.name && x == host.name && 1","filter_vars":{"x":"alexandersmbp2.int.netways.de","y":"42"}}' 'https://127.0.0.1:5665/v1/objects/services'
curl -ksSu root:123456 -o /dev/null -H 'Accept: application/json' -X GET -d    0,01s user 0,01s system 0% cpu 3,290 total
$

👍 The above one is 50x faster, just because no && 1

Config

for (i in range(100000)) {
        object Service i {
                host_name = NodeName
                check_command = "passive"
                enable_active_checks = false
        }
}

Copy link
Member

@yhabteab yhabteab left a comment

Choose a reason for hiding this comment

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

Apart from the comments below it's just fine, I'm not sure how this would in any way fix the memory leak referenced in the PR desc though.

lib/config/applyrule.hpp Outdated Show resolved Hide resolved
if (type == "Host") {
std::vector<const String *> targetNames;

if (ApplyRule::GetTargetHosts(subex.at(0).get(), targetNames, filter_vars)) {
Copy link
Member

Choose a reason for hiding this comment

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

Since the goal of this PR is reducing the performance overhead, you should favour subex[0] over subex.at(0).

Copy link
Member Author

Choose a reason for hiding this comment

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

Won't give much in addition, at the cost of SEGV -not just exception- if the code breaks in the future.

} else if (type == "Service") {
std::vector<std::pair<const String *, const String *>> targetNames;

if (ApplyRule::GetTargetServices(subex.at(0).get(), targetNames, filter_vars)) {
Copy link
Member

Choose a reason for hiding this comment

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

Same here!

Copy link
Member

Choose a reason for hiding this comment

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

I think it's better to use BOOST_(UN)EQUAL(), BOOST_REQUIRE_(UN)EQUAL() instead of BOOST_CHECK() as Julian suggested in another PR of yours.

Copy link
Member Author

Choose a reason for hiding this comment

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

Doesn't work for vectors unfortunately.

in addition to literal strings. This is for sandboxed filters with some
variables pre-set by the caller. They're "constant" in that scope, too.
test/config-apply.cpp Outdated Show resolved Hide resolved
test/config-apply.cpp Outdated Show resolved Hide resolved
test/config-apply.cpp Outdated Show resolved Hide resolved
test/config-apply.cpp Outdated Show resolved Hide resolved
actualServiceNames.emplace_back(*s.first, *s.second);
}

BOOST_CHECK(actualServiceNames == services);
Copy link
Member

Choose a reason for hiding this comment

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

It should also be possible to replace this with BOOST_CHECK_EQUAL_COLLECTIONS() by implementing the output operator for the std::pair<String, String> type.

template<>
struct boost::test_tools::tt_detail::print_log_value<std::pair<String, String>>
{
	inline void operator()(std::ostream& os, const std::pair<String, String>& hs)
	{
		os << hs.first << "!" << hs.second;
	}
};

Copy link
Member Author

Choose a reason for hiding this comment

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

Not bad! Please open a PR into my PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

Or push directly here. At your option.

…TIONS()

to show the value diff in case of mismatch.

Co-authored-by: Yonas Habteab <[email protected]>
Copy link
Member

@yhabteab yhabteab left a comment

Choose a reason for hiding this comment

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

As an optimisation of the object lookup for simple API filters targeting the Service or/and Host names LGTM! However, this is unlikely to fix the memory leak described in the community form, as we don't even know where it is leaking.

@Al2Klimov Al2Klimov merged commit 949d983 into master Dec 19, 2023
25 checks passed
@Al2Klimov Al2Klimov deleted the targeted-api-filter branch December 19, 2023 14:18
@Al2Klimov Al2Klimov added backported Fix was included in a bugfix release and removed consider backporting Should be considered for inclusion in a bugfix release labels May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/api REST API area/configuration DSL, parser, compiler, error handling backported Fix was included in a bugfix release cla/signed core/quality Improve code, libraries, algorithms, inline docs enhancement New feature or request ref/IP
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants