feature(balancing): Add an optional threshold in percent for balancing

Fixes: #342
This commit is contained in:
gyptazy
2025-10-23 11:52:01 +02:00
parent b9be405194
commit 30e3b66be9
4 changed files with 37 additions and 2 deletions

View File

@@ -0,0 +1,2 @@
added:
- Add an optional memory balancing threshold (@gyptazy). [#342]

View File

@@ -282,6 +282,7 @@ The following options can be set in the configuration file `proxlb.yaml`:
| | balance_types | | ['vm', 'ct'] | `List` | Defined the types of guests that should be honored. [values: `vm`, `ct`]|
| | max_job_validation | | 1800 | `Int` | How long a job validation may take in seconds. (default: 1800) |
| | balanciness | | 10 | `Int` | The maximum delta of resource usage between node with highest and lowest usage. |
| | memory_threshold | | 75 | `Int` | The maximum threshold (in percent) that needs to be hit to perform balancing actions. (Optional) |
| | method | | memory | `Str` | The balancing method that should be used. [values: `memory` (default), `cpu`, `disk`]|
| | mode | | used | `Str` | The balancing mode that should be used. [values: `used` (default), `assigned`, `psi` (pressure)] |
| | psi | | { nodes: { memory: { pressure_full: 0.20, pressure_some: 0.20, pressure_spikes: 1.00 } } } | `Dict` | A dict of PSI based thresholds for nodes and guests |
@@ -326,6 +327,7 @@ balancing:
with_conntrack_state: True
balance_types: ['vm', 'ct']
max_job_validation: 1800
memory_threshold: 75
balanciness: 5
method: memory
mode: used

View File

@@ -27,8 +27,9 @@ balancing:
with_local_disks: True
with_conntrack_state: True
balance_types: ['vm', 'ct'] # 'vm' | 'ct'
max_job_validation: 1800
balanciness: 5
max_job_validation: 1800 # Maximum time (in seconds) a job validation may take
memory_threshold: 75 # Optional: Maximum threshold (in percent) to trigger balancing actions
balanciness: 5 # Maximum delta of resource usage between highest and lowest usage node
method: memory # 'memory' | 'cpu' | 'disk'
mode: used # 'assigned' | 'used' | 'psi'
# # PSI thresholds only apply when using mode 'psi'

View File

@@ -194,9 +194,39 @@ class Calculations:
if mode == "assigned":
method_value = [node_meta[f"{method}_{mode}_percent"] for node_meta in proxlb_data["nodes"].values()]
if proxlb_data["meta"]["balancing"].get(f"{method}_threshold", None):
threshold = proxlb_data["meta"]["balancing"].get(f"{method}_threshold")
highest_usage_node = max(proxlb_data["nodes"].values(), key=lambda x: x[f"{method}_{mode}_percent"])
highest_node_value = highest_usage_node[f"{method}_{mode}_percent"]
if highest_node_value >= threshold:
logger.debug(f"Guest balancing is required. Highest {method} usage node {highest_usage_node['name']} is above the defined threshold of {threshold}% with a value of {highest_node_value}%.")
proxlb_data["meta"]["balancing"]["balance"] = True
else:
logger.debug(f"Guest balancing is ok. Highest {method} usage node {highest_usage_node['name']} is below the defined threshold of {threshold}% with a value of {highest_node_value}%.")
proxlb_data["meta"]["balancing"]["balance"] = False
else:
logger.debug(f"No {method} threshold defined for balancing. Skipping threshold check.")
elif mode == "used":
method_value = [node_meta[f"{method}_{mode}_percent"] for node_meta in proxlb_data["nodes"].values()]
if proxlb_data["meta"]["balancing"].get(f"{method}_threshold", None):
threshold = proxlb_data["meta"]["balancing"].get(f"{method}_threshold")
highest_usage_node = max(proxlb_data["nodes"].values(), key=lambda x: x[f"{method}_{mode}_percent"])
highest_node_value = highest_usage_node[f"{method}_{mode}_percent"]
if highest_node_value >= threshold:
logger.debug(f"Guest balancing is required. Highest {method} usage node {highest_usage_node['name']} is above the defined threshold of {threshold}% with a value of {highest_node_value}%.")
proxlb_data["meta"]["balancing"]["balance"] = True
else:
logger.debug(f"Guest balancing is ok. Highest {method} usage node {highest_usage_node['name']} is below the defined threshold of {threshold}% with a value of {highest_node_value}%.")
proxlb_data["meta"]["balancing"]["balance"] = False
else:
logger.debug(f"No {method} threshold defined for balancing. Skipping threshold check.")
elif mode == "psi":
method_value = [node_meta[f"{method}_pressure_full_spikes_percent"] for node_meta in proxlb_data["nodes"].values()]
any_node_hot = any(node.get(f"{method}_pressure_hot", False) for node in proxlb_data["nodes"].values())