-
Notifications
You must be signed in to change notification settings - Fork 114
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
flaky test: compliance scanner #540
Comments
Let me have a look at this bad boy! |
Looking into this issue, I noticed that for this nodes list query: MANAGER_GRPC nodes, :list, Nodes::Query.new(filters: [
Common::Filter.new(key: "state", values: ["TERMINATED"]),
Common::Filter.new(key: "environment", values: ["trou"]),
Common::Filter.new(key: "group", values: ["mak", "do"])
]) the node manager is doing the following SQL query to get the list of nodes: SELECT
n.connection_error,
n.id,
n.last_contact,
n.last_job,
n.manager,
n.name,
n.platform,
n.platform_version,
n.report_id,
n.source_state,
n.status,
n.target_config,
n.last_scan,
n.last_run,
n.projects_data,
COALESCE(('[' || string_agg('{"key":"' || t.key || '"' || ',"value": "' || t.value || '"}', ',') || ']'), '[]') :: JSON AS tags,
COALESCE(array_to_json(array_remove(array_agg(DISTINCT m.manager_id), NULL)), '[]') AS manager_ids,
COALESCE(array_to_json(array_remove(array_agg(p.project_id), NULL)), '[]') AS projects,
COUNT(*) OVER () AS total_count
FROM nodes n
LEFT JOIN nodes_tags nt ON n.id = nt.node_id
LEFT JOIN tags t ON t.id = nt.tag_id
LEFT JOIN node_managers_nodes m on n.id = m.node_id
LEFT JOIN nodes_projects np on np.node_id = n.id
LEFT JOIN projects p on np.project_id = p.id
WHERE (n.source_state IN ('TERMINATED') AND t.key LIKE 'group' AND t.value LIKE 'mak%' OR t.value LIKE 'do%' OR t.key LIKE 'environment' AND t.value LIKE 'trou%')
GROUP BY n.id
ORDER BY LOWER(n.name) asc
LIMIT 100
OFFSET 0;
connection_error | id | last_contact | last_job | manager | name | platform | platform_version | report_id | source_state | status | target_config |
last_scan | last_run | projects_data | tags | manager_ids | projects | total_count
------------------+--------------------------------------+---------------------+--------------------------------------+----------+-------------------------+----------+------------------+-----------+--------------+-------------+------------------------------------------------------------------------------------------------+-------------------------------------
--------------------------------------------------------------------------------------------+----------+---------------+---------------------------------------------------------------------------------------------------------------+------------------------------------------+----------+-------------
| 509f845c-f833-4635-9e21-5dd30e1141c4 | 2019-06-17 10:31:07 | 48cdefa5-08ce-4d27-943d-819e38a074e1 | automate | My Existing Docker Node | debian | 9.7 | | TERMINATED | unreachable | {"secrets":["098ecb0d-f440-4f27-acd2-386269c863e1"],"backend":"docker","host":"cc_pg"} | {"ID":"f6046a69-b7a0-4b88-9b89-8e0bc
9f7cbc6","Status":"SKIPPED","PenultimateStatus":"SKIPPED","EndTime":"2019-06-17T10:31:06Z"} | {} | null | [{"key":"group","value": "doers"},{"key":"group","value": "makers"},{"key":"environment","value": "trouble"}] | ["e69dc612-7e67-43f2-9b19-256afd385820"] | [] | 2
unknown error | 6a5d83e3-0315-479e-873c-2690a05648e7 | 0001-01-01 00:00:00 | 79ce54b9-3480-4942-aa14-8905d03c0796 | automate | My Missing Docker Node | | | | | unreachable | {"secrets":["098ecb0d-f440-4f27-acd2-386269c863e1"],"backend":"docker","host":"cc_pggggggggg"} | {}
| {} | [] | [{"key":"environment","value": "trouble"}] | ["e69dc612-7e67-43f2-9b19-256afd385820"] | [] | 2
(2 rows) Because of the lack of parentheses and the ORed
0 nodes are returned. This is because of the multiple tag based conditions that are applied to each row after the LEFT join. |
I managed to come up with this query that uses an EXIST + SELECT for each tag used for filtering: SELECT
n.connection_error,
n.id,
n.last_contact,
n.last_job,
n.manager,
n.name,
n.platform,
n.platform_version,
n.report_id,
n.source_state,
n.status,
n.target_config,
n.last_scan,
n.last_run,
n.projects_data,
COALESCE(('[' || string_agg('{"key":"' || t.key || '"' || ',"value": "' || t.value || '"}', ',') || ']'), '[]') :: JSON AS tags,
COALESCE(array_to_json(array_remove(array_agg(DISTINCT m.manager_id), NULL)), '[]') AS manager_ids,
COALESCE(array_to_json(array_remove(array_agg(p.project_id), NULL)), '[]') AS projects,
COUNT(*) OVER () AS total_count
FROM nodes n
LEFT JOIN nodes_tags nt ON n.id = nt.node_id
LEFT JOIN tags t ON t.id = nt.tag_id
LEFT JOIN node_managers_nodes m on n.id = m.node_id
LEFT JOIN nodes_projects np on np.node_id = n.id
LEFT JOIN projects p on np.project_id = p.id
WHERE
n.source_state IN ('TERMINATED') AND
EXISTS (SELECT NULL FROM nodes_tags nt LEFT JOIN tags t ON nt.tag_id = t.id WHERE nt.node_id = n.id AND t.key LIKE 'environment' AND t.value LIKE 'trou%') AND
EXISTS (SELECT NULL FROM nodes_tags nt LEFT JOIN tags t ON nt.tag_id = t.id WHERE nt.node_id = n.id AND t.key LIKE 'group' AND (t.value LIKE 'mak%' OR t.value LIKE 'do%'))
GROUP BY n.id
ORDER BY LOWER(n.name) asc
LIMIT 100
OFFSET 0;
connection_error | id | last_contact | last_job | manager | name | platform | platform_version | report_id | source_state | status | target_config |
last_scan | last_run | projects_data | tags | manager_ids | projects | total_count
------------------+--------------------------------------+---------------------+--------------------------------------+----------+-------------------------+----------+------------------+-----------+--------------+-------------+----------------------------------------------------------------------------------------+---------------------------------------------
------------------------------------------------------------------------------------+----------+---------------+---------------------------------------------------------------------------------------------------------------+------------------------------------------+----------+-------------
| 509f845c-f833-4635-9e21-5dd30e1141c4 | 2019-06-17 10:31:07 | 48cdefa5-08ce-4d27-943d-819e38a074e1 | automate | My Existing Docker Node | debian | 9.7 | | TERMINATED | unreachable | {"secrets":["098ecb0d-f440-4f27-acd2-386269c863e1"],"backend":"docker","host":"cc_pg"} | {"ID":"f6046a69-b7a0-4b88-9b89-8e0bc9f7cbc6"
,"Status":"SKIPPED","PenultimateStatus":"SKIPPED","EndTime":"2019-06-17T10:31:06Z"} | {} | null | [{"key":"group","value": "doers"},{"key":"group","value": "makers"},{"key":"environment","value": "trouble"}] | ["e69dc612-7e67-43f2-9b19-256afd385820"] | [] | 1
(1 row) which correctly matches only the I'm concerned about the performance of this query due to the nested SELECT calls within WHERE EXISTS. |
ooooh nice find @alexpop !! |
I paired with Rick today and looked for an alternative solution to the EXISTS with inner SELECTS, which was returning in 3 minutes for tag only filtering for 30k nodes. We came up with the following SELECT with HAVING which we saw up to 100 times faster than the inner SELECTS version: SELECT
n.connection_error,
n.id,
n.last_contact,
n.last_job,
n.manager,
n.name,
n.platform,
n.platform_version,
n.report_id,
n.source_state,
n.status,
n.target_config,
n.last_scan,
n.last_run,
n.projects_data,
COALESCE(('[' || string_agg('{"key":"' || t.key || '"' || ',"value": "' || t.value || '"}', ',') || ']'), '[]') :: JSON AS tags,
COALESCE(array_to_json(array_remove(array_agg(DISTINCT m.manager_id), NULL)), '[]') AS manager_ids,
COALESCE(array_to_json(array_remove(array_agg(p.project_id), NULL)), '[]') AS projects,
COUNT(*) OVER () AS total_count
FROM nodes n
LEFT JOIN nodes_tags nt ON n.id = nt.node_id
LEFT JOIN tags t ON t.id = nt.tag_id
LEFT JOIN node_managers_nodes m on n.id = m.node_id
LEFT JOIN nodes_projects np on np.node_id = n.id
LEFT JOIN projects p on np.project_id = p.id
WHERE (n.source_state IN ('TERMINATED'))
GROUP BY n.id
HAVING (array_agg(t.key || ' ' || t.value) @> ARRAY ['group doers'] OR array_agg(t.key || ' ' || t.value) @> ARRAY ['group makers']) AND array_agg(t.key || ' ' || t.value) @> ARRAY ['environment trouble']
ORDER BY LOWER(n.name) asc
LIMIT 100
OFFSET 0; |
Nice! We could also be missing some indexes for all these different types of queries. |
The I switched to SELECT
n.connection_error,
n.id,
n.last_contact,
n.last_job,
n.manager,
n.name,
n.platform,
n.platform_version,
n.report_id,
n.source_state,
n.status,
n.target_config,
n.last_scan,
n.last_run,
n.projects_data,
COALESCE(('[' || string_agg('{"key":"' || t.key || '"' || ',"value": "' || t.value || '"}', ',') || ']'), '[]') :: JSON AS tags,
COALESCE(array_to_json(array_remove(array_agg(DISTINCT m.manager_id), NULL)), '[]') AS manager_ids,
COALESCE(array_to_json(array_remove(array_agg(p.project_id), NULL)), '[]') AS projects,
COUNT(*) OVER () AS total_count
FROM nodes n
LEFT JOIN nodes_tags nt ON n.id = nt.node_id
LEFT JOIN tags t ON t.id = nt.tag_id
LEFT JOIN node_managers_nodes m on n.id = m.node_id
LEFT JOIN nodes_projects np on np.node_id = n.id
LEFT JOIN projects p on np.project_id = p.id
WHERE (n.source_state IN ('TERMINATED'))
GROUP BY n.id
HAVING (string_agg(',' || t.key || ':' || t.value, '') LIKE '%,environment:trou%') AND (string_agg(',' || t.key || ':' || t.value, '') LIKE '%,group:mak%' OR string_agg(',' || t.key || ':' || t.value, '') LIKE '%,group:do%')
ORDER BY LOWER(n.name) asc
LIMIT 100
OFFSET 0; |
For NOT to work when nodes have no tags, I had to concat the value of
|
Looks like this was passing through and generating an invalid postgres query with my refactor:
I updated the code to search for any tag value in this case. |
I added a number of extra tests for tags filtering. Did another performance test with 10k nodes with 11k tags between them. Same filter condition but with |
Node manager integration tests are failing on #662 because we issue a GetNodes here:
with filters:
These used to be OR'ed before and returned two nodes. They are now AND'ed and we don't have nodes that share those two tags. Before I update the test I want to clarify that AND is what we want here. |
thinking through this.. So, for consistency, AND for tags of different keys, OR for tags of same keys - so ya, AND is what we want here. might be worth noting that logic in the docs: https://github.com/chef/automate/blob/master/components/automate-chef-io/content/docs/nodes.md |
Thank you for confirming Victoria. |
User Story
https://buildkite.com/chef-oss/chef-automate-master-verify/builds/1796#db69b150-b637-418c-837e-4e70f13322c5
we've seen this multiple times.
i believe it's related to https://github.com/chef/automate/pull/377/files and #399 ....sorry :/
The text was updated successfully, but these errors were encountered: