chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:17:40 +08:00
commit f1825c8ceb
10096 changed files with 2364182 additions and 0 deletions
@@ -0,0 +1,43 @@
from typing import TYPE_CHECKING, List
from .backpressure_policy import BackpressurePolicy
from .concurrency_cap_backpressure_policy import ConcurrencyCapBackpressurePolicy
from .downstream_capacity_backpressure_policy import (
DownstreamCapacityBackpressurePolicy,
)
from .resource_budget_backpressure_policy import ResourceBudgetBackpressurePolicy
from ray.data.context import DataContext
if TYPE_CHECKING:
from ray.data._internal.execution.resource_manager import ResourceManager
from ray.data._internal.execution.streaming_executor_state import Topology
# Default enabled backpressure policies and its config key.
# Use `DataContext.set_config` to config it.
ENABLED_BACKPRESSURE_POLICIES = [
ConcurrencyCapBackpressurePolicy,
ResourceBudgetBackpressurePolicy,
DownstreamCapacityBackpressurePolicy,
]
ENABLED_BACKPRESSURE_POLICIES_CONFIG_KEY = "backpressure_policies.enabled"
def get_backpressure_policies(
data_context: DataContext,
topology: "Topology",
resource_manager: "ResourceManager",
) -> List[BackpressurePolicy]:
policies = data_context.get_config(
ENABLED_BACKPRESSURE_POLICIES_CONFIG_KEY, ENABLED_BACKPRESSURE_POLICIES
)
return [policy(data_context, topology, resource_manager) for policy in policies]
__all__ = [
"BackpressurePolicy",
"ConcurrencyCapBackpressurePolicy",
"DownstreamCapacityBackpressurePolicy",
"ENABLED_BACKPRESSURE_POLICIES_CONFIG_KEY",
"get_backpressure_policies",
]
@@ -0,0 +1,70 @@
from abc import ABC
from typing import TYPE_CHECKING, Optional
from ray.data.context import DataContext
if TYPE_CHECKING:
from ray.data._internal.execution.interfaces.physical_operator import (
PhysicalOperator,
)
from ray.data._internal.execution.resource_manager import ResourceManager
from ray.data._internal.execution.streaming_executor_state import Topology
class BackpressurePolicy(ABC):
"""Interface for back pressure policies."""
@property
def name(self) -> str:
"""Human-readable name for UX/progress bar display.
Defaults to the class name. Subclasses can override for a custom name.
"""
return type(self).__name__
def __init__(
self,
data_context: DataContext,
topology: "Topology",
resource_manager: "ResourceManager",
):
"""Initialize the backpressure policy.
Args:
data_context: The data context.
topology: The execution topology.
resource_manager: The resource manager.
"""
self._data_context = data_context
self._topology = topology
self._resource_manager = resource_manager
def can_add_input(self, op: "PhysicalOperator") -> bool:
"""Determine if we can add a new input to the operator. If returns False, the
operator will be backpressured and will not be able to run new tasks.
Used in `streaming_executor_state.py::select_operator_to_run()`.
Returns: True if we can add a new input to the operator, False otherwise.
Note, if multiple backpressure policies are enabled, the operator will be
backpressured if any of the policies returns False.
"""
return True
def max_task_output_bytes_to_read(self, op: "PhysicalOperator") -> Optional[int]:
"""Return the maximum bytes of pending task outputs can be read for
the given operator. None means no limit.
This is used for output backpressure to limit how much data an operator
can read from its running tasks.
Note, if multiple backpressure policies return non-None values for an operator,
the minimum of those values will be used as the limit.
Args:
op: The operator to get the limit for.
Returns:
The maximum bytes that can be read, or None if no limit.
"""
return None
@@ -0,0 +1,253 @@
import logging
import math
import warnings
from collections import defaultdict
from typing import TYPE_CHECKING, Dict
from .backpressure_policy import BackpressurePolicy
from .downstream_capacity_backpressure_policy import (
get_available_object_store_budget_fraction,
)
from ray._common.utils import env_float
from ray.data._internal.execution.operators.map_operator import MapOperator
from ray.data._internal.execution.operators.task_pool_map_operator import (
TaskPoolMapOperator,
)
from ray.util.annotations import Deprecated, RayDeprecationWarning
if TYPE_CHECKING:
from ray.data._internal.execution.interfaces.physical_operator import (
PhysicalOperator,
)
from ray.data._internal.execution.operators.map_operator import MapOperator
logger = logging.getLogger(__name__)
@Deprecated(
message="ConcurrencyCapBackpressurePolicy is deprecated and will be removed "
"on or after Ray 2.59.",
)
class ConcurrencyCapBackpressurePolicy(BackpressurePolicy):
"""A backpressure policy that caps the concurrency of each operator.
This policy dynamically limits the number of concurrent tasks per operator
based on the output queue growth rate.
- Maintain asymmetric EWMA of total enqueued output bytes as the
typical level: `level`.
- Maintain asymmetric EWMA of absolute residual vs the *previous* level as a
scale proxy: `dev = EWMA(|q - level_prev|)`.
- Define deadband: Deadband is the acceptable range of the output queue size
around the typical level where the queue size is expected to stay stable.
deadband [lower, upper] = [level - K_DEV*dev, level + K_DEV*dev].
- If q > upper -> target cap = running - BACKOFF_FACTOR (back off)
If q < lower -> target cap = running + RAMPUP_FACTOR (ramp up)
Else -> target cap = running (hold)
- Apply user-configured max concurrency cap, admit iff running < target cap.
NOTE: Only support setting concurrency cap for `TaskPoolMapOperator` for now.
TODO(chengsu): Consolidate with actor scaling logic of `ActorPoolMapOperator`.
"""
@property
def name(self) -> str:
return "ConcurrencyCap"
# Smoothing factor for the asymmetric EWMA (slow fall, faster rise).
EWMA_ALPHA = env_float("RAY_DATA_CONCURRENCY_CAP_EWMA_ALPHA", 0.1)
EWMA_ALPHA_UP = 1.0 - (1.0 - EWMA_ALPHA) ** 2 # fast rise
# Deadband width in units of the EWMA absolute deviation estimate.
K_DEV = env_float("RAY_DATA_CONCURRENCY_CAP_K_DEV", 1.0)
# Factor to back off when the queue is too large.
BACKOFF_FACTOR = env_float("RAY_DATA_CONCURRENCY_CAP_BACKOFF_FACTOR", 1)
# Factor to ramp up when the queue is too small.
RAMPUP_FACTOR = env_float("RAY_DATA_CONCURRENCY_CAP_RAMPUP_FACTOR", 1)
# Threshold for per-Op object store budget (available) vs total
# (available / total) ratio to enable dynamic output queue size backpressure.
AVAILABLE_OBJECT_STORE_BUDGET_THRESHOLD = env_float(
"RAY_DATA_CONCURRENCY_CAP_AVAILABLE_OBJECT_STORE_BUDGET_THRESHOLD", 0.1
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Configured per-operator caps (+inf if unset).
self._concurrency_caps: Dict["PhysicalOperator", float] = {}
# EWMA state for level
self._q_level_nbytes: Dict["PhysicalOperator", float] = defaultdict(float)
# EWMA state for dev
self._q_level_dev: Dict["PhysicalOperator", float] = defaultdict(float)
# Per-operator cached threshold (bootstrapped from first sample).
self._queue_level_thresholds: Dict["PhysicalOperator", int] = defaultdict(int)
# Last effective cap for change logs.
self._last_effective_caps: Dict["PhysicalOperator", int] = {}
# Initialize caps from operators (infinite if unset)
for op, _ in self._topology.items():
if (
isinstance(op, TaskPoolMapOperator)
and op.get_max_concurrency_limit() is not None
):
self._concurrency_caps[op] = op.get_max_concurrency_limit()
else:
self._concurrency_caps[op] = float("inf")
# Whether to cap the concurrency of an operator based on its and downstream's queue size.
self.enable_dynamic_output_queue_size_backpressure = (
self._data_context.enable_dynamic_output_queue_size_backpressure
)
if self.enable_dynamic_output_queue_size_backpressure:
warnings.warn(
"ConcurrencyCapBackpressurePolicy is deprecated and will be "
"removed on or after Ray 2.59.",
RayDeprecationWarning,
stacklevel=2,
)
dynamic_output_queue_size_backpressure_configs = ""
if self.enable_dynamic_output_queue_size_backpressure:
dynamic_output_queue_size_backpressure_configs = (
f", EWMA_ALPHA={self.EWMA_ALPHA}, K_DEV={self.K_DEV}, "
f"BACKOFF_FACTOR={self.BACKOFF_FACTOR}, RAMPUP_FACTOR={self.RAMPUP_FACTOR}, "
f"AVAILABLE_OBJECT_STORE_BUDGET_THRESHOLD={self.AVAILABLE_OBJECT_STORE_BUDGET_THRESHOLD}"
)
logger.debug(
f"ConcurrencyCapBackpressurePolicy caps: {self._concurrency_caps}, "
f"enabled: {self.enable_dynamic_output_queue_size_backpressure}{dynamic_output_queue_size_backpressure_configs}"
)
def _update_ewma_asymmetric(self, prev_value: float, sample: float) -> float:
"""
Update EWMA with asymmetric behavior: fast rise, slow fall.
Args:
prev_value: Previous EWMA value
sample: New sample value
Returns:
Updated EWMA value
"""
if prev_value <= 0:
return sample
# fast rise if sample > prev_value, slow fall otherwise
alpha = self.EWMA_ALPHA_UP if sample > prev_value else self.EWMA_ALPHA
return (1 - alpha) * prev_value + alpha * sample
def _update_level_and_dev(self, op: "PhysicalOperator", q_bytes: int) -> None:
"""Update EWMA level and dev (residual w.r.t. previous level)."""
q = float(q_bytes)
level_prev = self._q_level_nbytes[op]
dev_prev = self._q_level_dev[op]
# Deviation vs the previous level
dev_sample = abs(q - level_prev) if level_prev > 0 else 0.0
dev = self._update_ewma_asymmetric(dev_prev, dev_sample)
# Now update the level itself
level = self._update_ewma_asymmetric(level_prev, q)
self._q_level_nbytes[op] = level
self._q_level_dev[op] = dev
# For visibility, store the integer center of the band
self._queue_level_thresholds[op] = max(1, int(level))
def can_add_input(self, op: "PhysicalOperator") -> bool:
"""Return whether `op` may accept another input now."""
num_tasks_running = op.metrics.num_tasks_running
# Skip dynamic backpressure if:
# - Not a MapOperator
# - Not eligible for Op for Backpressure
# - Dynamic backpressure based on output queue size is disabled
# - Downstream is a materializing op which requires full materialization
if (
not isinstance(op, MapOperator)
or not self._resource_manager.is_op_eligible(op)
or not self.enable_dynamic_output_queue_size_backpressure
or self._resource_manager._is_blocking_materializing_op(op)
):
return num_tasks_running < self._concurrency_caps[op]
# For this Op, if the objectstore budget (available) to total
# ratio is above threshold, skip dynamic output queue size backpressure.
available_budget_fraction = get_available_object_store_budget_fraction(
self._resource_manager, op, consider_downstream_ineligible_ops=True
)
if (
available_budget_fraction is not None
and available_budget_fraction > self.AVAILABLE_OBJECT_STORE_BUDGET_THRESHOLD
):
# If the objectstore budget (available) to total
# ratio is above threshold, skip dynamic output queue size
# backpressure, but still enforce the configured cap.
return num_tasks_running < self._concurrency_caps[op]
# Current total queued bytes (this op + downstream)
current_queue_size_bytes = self._resource_manager.get_mem_op_internal(
op
) + self._resource_manager.get_mem_op_outputs(
op, include_ineligible_downstream=True
)
# Update EWMA state (level & dev) and compute effective cap. Note that
# we don't update the EWMA state if the objectstore budget (available) vs total
# ratio is above threshold, because the level and dev adjusts quickly.
self._update_level_and_dev(op, current_queue_size_bytes)
effective_cap = self._effective_cap(
op, num_tasks_running, current_queue_size_bytes
)
last = self._last_effective_caps.get(op, None)
if last != effective_cap:
logger.debug(
f"Cap change {op.name}: {last if last is not None else 'None'} -> "
f"{effective_cap} (running={num_tasks_running}, queue={current_queue_size_bytes}, "
f"thr={self._queue_level_thresholds[op]})"
)
self._last_effective_caps[op] = effective_cap
return num_tasks_running < effective_cap
def _effective_cap(
self,
op: "PhysicalOperator",
num_tasks_running: int,
current_queue_size_bytes: int,
) -> int:
"""A simple controller around EWMA level.
Args:
op: The operator to compute the effective cap for.
num_tasks_running: The number of tasks currently running.
current_queue_size_bytes: Current total queued bytes for this operator + downstream.
Returns:
The effective cap.
"""
cap_cfg = self._concurrency_caps[op]
level = float(self._q_level_nbytes[op])
dev = max(1.0, float(self._q_level_dev[op]))
upper = level + self.K_DEV * dev
lower = level - self.K_DEV * dev
if current_queue_size_bytes > upper:
# back off
target = num_tasks_running - self.BACKOFF_FACTOR
elif current_queue_size_bytes < lower:
# ramp up
target = num_tasks_running + self.RAMPUP_FACTOR
else:
# hold
target = num_tasks_running
# Clamp to [1, configured_cap]
target = max(1, target)
if not math.isinf(cap_cfg):
target = min(target, int(cap_cfg))
return int(target)
@@ -0,0 +1,229 @@
import logging
from typing import TYPE_CHECKING, Optional
from .backpressure_policy import BackpressurePolicy
from ray._common.utils import env_float
from ray.data._internal.execution.resource_manager import (
ResourceManager,
)
from ray.data.context import DataContext
if TYPE_CHECKING:
from ray.data._internal.execution.interfaces.physical_operator import (
PhysicalOperator,
)
from ray.data._internal.execution.streaming_executor_state import Topology
logger = logging.getLogger(__name__)
def get_available_object_store_budget_fraction(
resource_manager: "ResourceManager",
op: "PhysicalOperator",
consider_downstream_ineligible_ops: bool,
) -> Optional[float]:
"""Get available object store memory budget fraction for the operator.
Args:
resource_manager: The resource manager to use.
op: The operator to get the budget fraction for.
consider_downstream_ineligible_ops: If True, include downstream ineligible
ops in the calculation. If False, only consider this op's usage/budget.
Returns:
The available budget fraction, or None if not available.
"""
op_usage = resource_manager.get_op_usage(
op, include_ineligible_downstream=consider_downstream_ineligible_ops
)
op_budget = resource_manager.get_budget(op)
if op_usage is None or op_budget is None:
return None
total_usage = op_usage.object_store_memory
total_budget = op_budget.object_store_memory
total_mem = total_usage + total_budget
if total_mem == 0:
return None
return total_budget / total_mem
def get_utilized_object_store_budget_fraction(
resource_manager: "ResourceManager",
op: "PhysicalOperator",
consider_downstream_ineligible_ops: bool,
) -> Optional[float]:
"""Get utilized object store memory budget fraction for the operator.
Args:
resource_manager: The resource manager to use.
op: The operator to get the utilized fraction for.
consider_downstream_ineligible_ops: If True, include downstream ineligible
ops in the calculation. If False, only consider this op's usage/budget.
Returns:
The utilized budget fraction, or None if not available.
"""
available_fraction = get_available_object_store_budget_fraction(
resource_manager,
op,
consider_downstream_ineligible_ops=consider_downstream_ineligible_ops,
)
if available_fraction is None:
return None
return 1 - available_fraction
class DownstreamCapacityBackpressurePolicy(BackpressurePolicy):
"""Backpressure policy based on downstream processing capacity.
To backpressure a given operator, use queue size build up / downstream capacity ratio.
This ratio represents the upper limit of buffering in object store between pipeline stages
to optimize for throughput.
"""
# Threshold for per-Op object store budget utilization vs total
# (utilization / total) ratio to enable downstream capacity backpressure.
OBJECT_STORE_BUDGET_UTIL_THRESHOLD = env_float(
"RAY_DATA_DOWNSTREAM_CAPACITY_OBJECT_STORE_BUDGET_UTIL_THRESHOLD", 0.5
)
@property
def name(self) -> str:
return "DownstreamCapacity"
def __init__(
self,
data_context: DataContext,
topology: "Topology",
resource_manager: "ResourceManager",
):
super().__init__(data_context, topology, resource_manager)
self._backpressure_capacity_ratio = (
self._data_context.downstream_capacity_backpressure_ratio
)
self._prev_should_backpressure: dict["PhysicalOperator", bool] = {}
if self._backpressure_capacity_ratio is not None:
logger.debug(
"DownstreamCapacityBackpressurePolicy enabled with backpressure "
f"capacity ratio: {self._backpressure_capacity_ratio}"
)
def _get_queue_size_bytes(self, op: "PhysicalOperator") -> int:
"""Get the output current queue size
(this operator + ineligible downstream operators) in bytes for the given operator.
"""
op_outputs_usage = self._topology[op].output_queue_bytes()
# Also account the downstream ineligible operators' memory usage.
op_outputs_usage += sum(
self._resource_manager.get_op_usage(next_op).object_store_memory
for next_op in self._resource_manager._get_downstream_ineligible_ops(op)
)
return op_outputs_usage
def _get_downstream_capacity_size_bytes(self, op: "PhysicalOperator") -> int:
"""Get the downstream capacity size for the given operator.
Downstream capacity size is the sum of the pending task inputs of the
downstream eligible operators.
If an output dependency is ineligible, skip it and recurse down to find
eligible output dependencies. If there are no output dependencies,
return external consumer bytes.
"""
if not op.output_dependencies:
# No output dependencies, return external consumer bytes.
return self._resource_manager.get_external_consumer_bytes()
total_capacity_size_bytes = 0
for output_dependency in op.output_dependencies:
if self._resource_manager.is_op_eligible(output_dependency):
# Output dependency is eligible, add its pending task inputs.
total_capacity_size_bytes += (
output_dependency.metrics.obj_store_mem_pending_task_inputs or 0
)
else:
# Output dependency is ineligible, recurse down to find eligible ops.
total_capacity_size_bytes += self._get_downstream_capacity_size_bytes(
output_dependency
)
return total_capacity_size_bytes
def _should_skip_backpressure(self, op: "PhysicalOperator") -> bool:
"""Check if backpressure should be skipped for the operator.
TODO(srinathk10): Extract this to common logic to skip invoking BackpressurePolicy.
"""
if self._backpressure_capacity_ratio is None:
# Downstream capacity backpressure is disabled.
return True
if not self._resource_manager.is_op_eligible(op):
# Operator is not eligible for backpressure.
return True
if self._resource_manager._is_blocking_materializing_op(op):
# Operator is materializing, so no need to perform backpressure.
return True
return False
def _get_queue_ratio(self, op: "PhysicalOperator") -> float:
"""Get queue/capacity ratio for the operator."""
queue_size_bytes = self._get_queue_size_bytes(op)
downstream_capacity_size_bytes = self._get_downstream_capacity_size_bytes(op)
if downstream_capacity_size_bytes == 0:
# No downstream capacity to backpressure against, so no backpressure.
return 0
return queue_size_bytes / downstream_capacity_size_bytes
def _should_apply_backpressure(self, op: "PhysicalOperator") -> bool:
"""Check if backpressure should be applied for the operator.
Returns True if backpressure should be applied, False otherwise.
"""
if self._should_skip_backpressure(op):
return False
utilized_budget_fraction = get_utilized_object_store_budget_fraction(
self._resource_manager, op, consider_downstream_ineligible_ops=True
)
queue_ratio = self._get_queue_ratio(op)
if (
utilized_budget_fraction is not None
and utilized_budget_fraction <= self.OBJECT_STORE_BUDGET_UTIL_THRESHOLD
):
# Utilized budget fraction is below threshold, so should skip backpressure.
result = False
else:
# Apply backpressure if queue ratio exceeds the threshold.
result = queue_ratio > self._backpressure_capacity_ratio
prev = self._prev_should_backpressure.get(op)
if prev != result:
queue_size_bytes = self._get_queue_size_bytes(op)
downstream_capacity_bytes = self._get_downstream_capacity_size_bytes(op)
logger.debug(
f"Backpressure change {op.name}: {prev} -> {result} "
f"(queue_ratio={queue_ratio:.2f}, {queue_size_bytes=}, "
f"{downstream_capacity_bytes=}, {utilized_budget_fraction=})"
)
self._prev_should_backpressure[op] = result
return result
def can_add_input(self, op: "PhysicalOperator") -> bool:
"""Determine if we can add input to the operator based on
downstream capacity.
"""
return not self._should_apply_backpressure(op)
def max_task_output_bytes_to_read(self, op: "PhysicalOperator") -> Optional[int]:
"""Return the maximum bytes of pending task outputs can be read for
the given operator. None means no limit."""
if self._should_apply_backpressure(op):
return 0
return None
@@ -0,0 +1,36 @@
import logging
from typing import TYPE_CHECKING, Optional
from .backpressure_policy import BackpressurePolicy
if TYPE_CHECKING:
from ray.data._internal.execution.interfaces.physical_operator import (
PhysicalOperator,
)
logger = logging.getLogger(__name__)
class ResourceBudgetBackpressurePolicy(BackpressurePolicy):
"""A backpressure policy based on resource budgets in ResourceManager."""
@property
def name(self) -> str:
return "ResourceBudget"
def can_add_input(self, op: "PhysicalOperator") -> bool:
if self._resource_manager._op_resource_allocator is not None:
return self._resource_manager._op_resource_allocator.can_submit_new_task(op)
return True
def max_task_output_bytes_to_read(self, op: "PhysicalOperator") -> Optional[int]:
"""Determine maximum bytes to read based on the resource budgets.
Args:
op: The operator to get the limit for.
Returns:
The maximum bytes that can be read, or None if no limit.
"""
return self._resource_manager.max_task_output_bytes_to_read(op)