-
Notifications
You must be signed in to change notification settings - Fork 7
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
Issue/7694 executor cleanup #7765
Conversation
src/inmanta/agent/config.py
Outdated
@@ -188,7 +189,7 @@ def is_executor_mode(value: str | AgentExecutorMode) -> AgentExecutorMode: | |||
3, | |||
"Maximum number of concurrent executors to keep per environment, per agent. If this limit is already reached " | |||
"when creating a new executor, the oldest one will be stopped first.", | |||
is_int, | |||
functools.partial(is_lower_bounded_int, 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use the docstring of the function to generate documentation, so this won't look so pretty in the docs
|
||
self.stopping: bool = False | ||
# We keep a reference to the periodic cleanup task to prevent it | ||
# from disappearing mid-execution https://docs.python.org/3.11/library/asyncio-task.html#creating-tasks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow, nice!
return p, parent_conn | ||
|
||
async def start(self) -> None: | ||
self.cleanup_job = asyncio.create_task(self.cleanup_inactive_executors()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in the join method, you could await self.cleanup_job
to make sure it is down.
self.executor_retention_time = inmanta.agent.config.agent_executor_retention_time.get() | ||
self.max_executors_per_agent = inmanta.agent.config.agent_executor_cap.get() | ||
|
||
self.stopping: bool = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already given more comment than I'm comfortable, so feel free to ignore this one: when used like this, I usually call this 'running' (and invert it) to make sure it doesn't only indicate 'stopping' but also 'stopped'
# Clean up executors after 3s of inactivity | ||
inmanta.agent.config.agent_executor_retention_time.set("3") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Increased this a bit because it was causing intempestive cleanup in the test case: the old executor is supposed to get cleaned up when the agent cap is reached, but with this setting too low, it would automatically get cleaned up before then.
@@ -397,7 +399,21 @@ async def serve() -> None: | |||
exit(0) | |||
|
|||
|
|||
class MPExecutor(executor.Executor): | |||
class PoolMember(abc.ABC): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Could use a docstring though.
@@ -518,7 +537,24 @@ async def get_facts(self, resource: "inmanta.agent.executor.ResourceDetails") -> | |||
return await self.connection.call(FactsCommand(resource)) | |||
|
|||
|
|||
class MPManager(executor.ExecutorManager[MPExecutor]): | |||
class PoolManager: | |||
def __init__(self, max_time: int, max_pool_size: int, min_pool_size: int): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def __init__(self, max_time: int, max_pool_size: int, min_pool_size: int): | |
def __init__(self, *, max_time: int, max_pool_size: int, min_pool_size: int): |
@@ -518,7 +537,24 @@ async def get_facts(self, resource: "inmanta.agent.executor.ResourceDetails") -> | |||
return await self.connection.call(FactsCommand(resource)) | |||
|
|||
|
|||
class MPManager(executor.ExecutorManager[MPExecutor]): | |||
class PoolManager: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
class PoolManager: | |
class PoolManager(abc.ABC): |
@@ -518,7 +537,24 @@ async def get_facts(self, resource: "inmanta.agent.executor.ResourceDetails") -> | |||
return await self.connection.call(FactsCommand(resource)) | |||
|
|||
|
|||
class MPManager(executor.ExecutorManager[MPExecutor]): | |||
class PoolManager: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest, I don't see the benefit of this class, or even the PoolMember
class, the way they're implemented now. Sure, they present a common interface, but it doesn't seem like we use the interface in any meaningful way?
- the
get_pool_members
method is not used - neither class offers any funcionality, meaning the cleanup methods are still custom
Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, this is not useful right now, I'll remove it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not entirely what I'm trying to say. I think the concept is very good, if it allows us to share behavior between both use cases. So when @wouterdb proposed it, I'm assuming that's what he had in mind? By removing it, we're back to square one, aren't we?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My review is blocked on Wouter's response to this discussion.
Processing this pull request |
Merged into branches master in 04983c4 |
# Description Closes #7694 Add 2 config options: - `executor_retention`: executors idle for longer than this get cleaned up - `executor_cap_per_agent`: if this limit is already reached when attempting to create a new executor for this agent, the oldest executor is first stopped. # Self Check: Strike through any lines that are not applicable (`~~line~~`) then check the box - [x] Attached issue to pull request - [x] Changelog entry - [x] Type annotations are present - [x] Code is clear and sufficiently documented - [x] No (preventable) type errors (check using make mypy or make mypy-diff) - [x] Sufficient test cases (reproduces the bug/tests the requested feature) - [x] Correct, in line with design ~~- [ ] End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )~~ ~~- [ ] If this PR fixes a race condition in the test suite, also push the fix to the relevant stable branche(s) (see [test-fixes](https://internal.inmanta.com/development/core/tasks/build-master.html#test-fixes) for more info)~~
Description
Closes #7694
Add 2 config options:
executor_retention
: executors idle for longer than this get cleaned upexecutor_cap_per_agent
: if this limit is already reached when attempting to create a new executor for this agent, the oldest executor is first stopped.Self Check:
Strike through any lines that are not applicable (
~~line~~
) then check the box- [ ] End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )- [ ] If this PR fixes a race condition in the test suite, also push the fix to the relevant stable branche(s) (see test-fixes for more info)