-
Notifications
You must be signed in to change notification settings - Fork 6.9k
[serve.llm] Prefix-aware scheduler [2/N] Configure PrefixAwareReplicaScheduler as default scheduler in LLMServer #52725
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
Merged
kouroshHakha
merged 131 commits into
ray-project:master
from
jujipotle:prefix-aware-scheduler
Jun 9, 2025
Merged
Changes from all commits
Commits
Show all changes
131 commits
Select commit
Hold shift + click to select a range
594a425
WIP
GeneDer e3fc0a9
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer 854af70
WIP: refactor locality minxin
GeneDer 4f44870
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer 33dc3a3
use context vars for tracking flags
GeneDer 603d074
serialize replica scheduler
GeneDer 33d7a53
fix update mulitplexed mode id
GeneDer 2fe7ddf
more refactor
GeneDer 2b4a89e
WIP: still breaking, need to test passing the _RequestSchedulingConte…
GeneDer df1ac0d
fix bug
jujipotle 762e7ce
Merge branch 'master' into prefix-aware-scheduler
jujipotle d69314d
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer 70b1953
begin migrate prefix aware
jujipotle 4105417
Opening up replica_scheduler
jujipotle 9b5e440
implement get_deployment_config
jujipotle 5b28af4
Minimal changes to swap in custom scheduler using llm config, since d…
jujipotle fea040a
Match requests based on internal_request_id
jujipotle b205643
Refactor prefix aware scheduler with newer design
jujipotle bf9d5c6
Wrap ObjectRefGenerator so callback can peek without iterating
jujipotle 60c9975
Fix callback for update tree to work with streaming responses
jujipotle 57dd69c
Clean up
jujipotle 28fee7d
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer 19eed43
Callback takes longer with streaming
jujipotle 75512bf
add get_deployment_config api and use the replica scheduler set onto …
GeneDer 632568e
lint
GeneDer 6b88c51
fix getting deployment config
GeneDer 3d58a07
fix the key passed into choose_replicas
GeneDer 2ff36e6
WIP
GeneDer d0142f4
WIP: fix some tests
GeneDer da6d85b
fix all tests
GeneDer 29886be
fix import
GeneDer 9090deb
WIP
GeneDer dfd3113
refactor common scheduler logics
GeneDer db54615
fix
GeneDer b9e022d
enable user to pass scheduling stats
GeneDer 60a31a4
try again
GeneDer 5a659a7
create alias for public replica scheduler module
GeneDer b6315ee
fix choose_replicas interface and add scheduling_stats property
GeneDer 40bcbc8
fix type
GeneDer da329b6
expose ReplicaID and ReplicaSchedulingInfo publically
GeneDer 2993285
expose the replica scheduler fields publically and allow the deployme…
GeneDer 9854c40
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer 0d7ec47
allow to reconfigure replica scheduler for a given handle
GeneDer fb8d416
add on_request_scheduled callback
GeneDer 34f86e0
allow the replica scheduler to configure to match on the exact request
GeneDer dad320c
refactor choose_replicas into choose_replicas_with_backoff and choose…
GeneDer cb03495
implement rank_replicas on multiplexed and locality mixin and update …
GeneDer 2f49b2f
create FIFOMixin and change the default to OOO scheduling
GeneDer 97c7b7c
add select_available_replicas to filter replicas unavailable to take …
GeneDer 139fd33
uncomment commented code
GeneDer 280d966
fix assign_request on LocalRouter
GeneDer e7e8983
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer f910031
call on_request_scheduled directly instead of allowing setting with .…
GeneDer 07ceeab
fix on_request_scheduled interface
GeneDer bef7948
Merge branch 'master' into prefix-aware-scheduler
jujipotle 0756a1c
Merge branch 'master' into prototype-custom-replica-scheduler
GeneDer d1dd98b
allow the controller to update scheduling stats
GeneDer 60d1df4
Merge branch 'genesu-prototype-custom-replica-scheduler' into prefix-…
jujipotle 5e58b11
fixes
GeneDer 935cd1f
allow the controller to update scheduling stats
GeneDer 587f0a6
fixes
GeneDer af8dbe1
update the choose_replicas to be a list of list instead of list of se…
GeneDer b0bd482
Clean up to match gene's work
jujipotle a49a794
add some newlines
jujipotle 526a7cf
Merge remote-tracking branch 'upstream/genesu-prototype-custom-replic…
jujipotle 7afc1bf
add requestSchedulingStatsPeriodS and requestSchedulingStatsTimeoutS …
GeneDer 77f81f4
fix controller test
GeneDer 60e0740
implement get_scheduling_stats onto MockReplicaActorWrapper
GeneDer bb84acc
update interface for ranking helpers
GeneDer b4b12d8
lint
GeneDer 935bbad
Merge branch 'prototype-custom-replica-scheduler' into genesu-prototy…
GeneDer b1f1c6d
fix recording scheduling stats
GeneDer 3befc66
Merge branch 'prototype-custom-replica-scheduler' into genesu-prototy…
GeneDer f48bc60
expoxe ReplicaResult publically
GeneDer 8f3ebbe
Merge branch 'prototype-custom-replica-scheduler' into genesu-prototy…
GeneDer 94e91c1
Get prefix aware scheduler working
jujipotle 8f2798a
Merge remote-tracking branch 'upstream/genesu-prototype-custom-replic…
jujipotle 67dea5c
small fixes
jujipotle eebe613
Make PrefixAware inherit Pow2, edit PrefixTree
jujipotle 8e601c9
Apply chat template before prefix matching
jujipotle 8b0d64e
Autoscaling, make vllm request processing an option and not default
jujipotle fe0f7bf
Eviction
jujipotle 8943097
Add eviction loop, benchmark pow2 against prefix aware
jujipotle 5b77a85
cleanup
jujipotle bcaa08c
Fix prompt extraction bug
jujipotle b242923
Linting
jujipotle 12be5a7
Fix eviction loop to be non-blocking, add eviction threshold and targ…
jujipotle 9186b4c
Move eviction loop to inside prefix tree, not scheduler. Add unit tes…
jujipotle 0e5412d
Clean up prefix aware scheduler init
jujipotle 37a098f
Merge branch 'master' into prefix-aware-scheduler
jujipotle cd655e2
E2E tests
jujipotle 6720a7e
Remove vllm metrics hacking
jujipotle c970e55
Prevent repetitive eviction loops, add on_request_scheduled to pow2
jujipotle 5cb7541
add E2E tests. Need to figure out why benchmark is slowing down.
jujipotle a0b9650
Fix benchmark overhead. Moved benchmarking scripts to ray.
jujipotle 2fdc715
Delete large files
jujipotle 81ffe6e
Delete some benchmarking results, start writing replication_tutorial.md
jujipotle 748956f
Replication tutorial
jujipotle 3b6bce4
Linting
jujipotle b903c5c
Clean up file directory structure; edit replication_tutorial instruct…
jujipotle 75cf584
Edit replication_tutorial.md
jujipotle 7f82e4f
Merge remote-tracking branch 'upstream/master' into prefix-aware-sche…
jujipotle 6fd2e8c
Merge remote-tracking branch 'upstream/master' into prefix-aware-sche…
jujipotle 0d35848
Manually make my PR similar to master
jujipotle da87c0f
Rename scheduler -> router
jujipotle 85d1f35
Rename replica scheduling -> request routing
jujipotle a33282f
Remove logs
jujipotle 598ce92
Update docs
jujipotle e1cb298
Explain how to visualize results
jujipotle 37f445a
Fix import error that was preventing CI from passing
jujipotle 109a23a
Merge master, resolve conflicts in test_prefix_tree.py and setup-dev.py
eicherseiji a12c869
Add type hints
eicherseiji 4458cbc
Lint
eicherseiji 361b381
Fix parameter name
eicherseiji dfb6166
Add back packages to link
eicherseiji 667c055
Merge branch 'master' into prefix-aware-scheduler
eicherseiji aaadb12
Adapt to new choose_replicas signature
eicherseiji 2efabd2
Move benchmark scripts to https://github.com/anyscale/serve-llm-repli…
eicherseiji a00c7dc
Remove injected stats logger
eicherseiji 25c3cdf
Convert warning logs to debugs
eicherseiji 5b21833
Expose prefix aware router via request_router __init__.py
eicherseiji ecaca59
Don't export PrefixAwareRequestRouter from serve since it's only used…
eicherseiji 16b2426
Merge branch 'master' into prefix-aware-scheduler
eicherseiji 62d9a47
Remove _track_metrics from PR
eicherseiji b4c3f2b
Update to call base class with warning that autoscaling not supported
eicherseiji 0cc56e4
Change name to PrefixAwarePow2ReplicaScheduler
eicherseiji aa3b071
Use a detached actor to avoid issues with actor lifetime
eicherseiji e7c8a34
Change name to end with 'Router'
eicherseiji dae5f4e
Autoscaling is now handled by the prefix tree with detached lifetime
eicherseiji e5c39c7
Set default LLMServer router to PowerOfTwoChoicesRequestRouter
eicherseiji fd089ac
Don't override Ray Serve router class in LLMDeployment
eicherseiji File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,17 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import asyncio | ||
| import logging | ||
| import os | ||
| from threading import RLock | ||
| from typing import Any, Dict, List, Optional, Tuple | ||
|
|
||
| import ray | ||
| from ray.serve._private.constants import ( | ||
| SERVE_LOGGER_NAME, | ||
| ) | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
| logger = logging.getLogger(SERVE_LOGGER_NAME) | ||
|
|
||
|
|
||
| class Node: | ||
|
|
@@ -86,12 +90,13 @@ def __init__(self) -> None: | |
| # Root is always the head of the LRU list for each tenant. | ||
| self.root: Node = Node() | ||
|
|
||
| # Tracks total character count per tenant. Can be used by the replica scheduler to determine which tenant to evict, and by how much. | ||
| # Tracks total character count per tenant. Can be used by the replica request router to determine which tenant to evict, and by how much. | ||
| # Also uses the keys to track the active tenants in the tree. | ||
| self.tenant_to_char_count: Dict[str, int] = {} | ||
|
|
||
| # LRU tracking - root is always the head, tail is the least recently used. | ||
| self.tenant_to_lru_tail: Dict[str, Optional[Node]] = {} | ||
| self._eviction_task: Optional[asyncio.Task] = None | ||
|
|
||
| @staticmethod | ||
| def _shared_prefix_count(a: str, b: str) -> int: | ||
|
|
@@ -113,34 +118,15 @@ def _get_lru_chain(self, tenant: str) -> List[Node]: | |
| Note: This method is intended to be used only in tests. | ||
| """ | ||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| return [] | ||
| nodes = [] | ||
| current_node = self.root | ||
| while current_node: | ||
| nodes.append(current_node) | ||
| current_node = current_node.tenant_to_older_node.get(tenant) | ||
| return nodes | ||
|
|
||
| def _add_tenant(self, tenant: str) -> None: | ||
| """ | ||
| Add a new tenant to the tree. | ||
|
|
||
| If the tenant already exists, this is a no-op with a warning log. | ||
|
|
||
| Args: | ||
| tenant: Tenant to add | ||
| """ | ||
| with self.lock: | ||
| if tenant in self.tenant_to_char_count: | ||
| logger.warning(f"Tenant '{tenant}' already exists. No action taken.") | ||
| return | ||
|
|
||
| self.tenant_to_char_count[tenant] = 0 | ||
| self.tenant_to_lru_tail[tenant] = self.root | ||
|
|
||
| # Initialize the root node as the head of the LRU list for this tenant | ||
| self.root.tenant_to_newer_node[tenant] = None | ||
| self.root.tenant_to_older_node[tenant] = None | ||
|
|
||
| def _insert_node_into_linked_list( | ||
| self, | ||
| node: Node, | ||
|
|
@@ -153,7 +139,9 @@ def _insert_node_into_linked_list( | |
| """ | ||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| logger.warning(f"Tenant '{tenant}' does not exist. No action taken.") | ||
| logger.debug( | ||
| f"[_insert_node_into_linked_list] Tenant '{tenant}' does not exist. No action taken." | ||
| ) | ||
| return | ||
|
|
||
| # Skip if node is the root | ||
|
|
@@ -178,7 +166,9 @@ def _remove_node_from_linked_list(self, node: Node, tenant: str) -> None: | |
| """ | ||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| logger.warning(f"Tenant '{tenant}' does not exist. No action taken.") | ||
| logger.debug( | ||
| f"[_remove_node_from_linked_list] Tenant '{tenant}' does not exist. No action taken." | ||
| ) | ||
| return | ||
|
|
||
| # Skip if node is the root | ||
|
|
@@ -216,11 +206,13 @@ def _remove_tenant_single_node(self, tenant: str, node: Node) -> int: | |
| """ | ||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| logger.warning(f"Tenant '{tenant}' does not exist. No action taken.") | ||
| logger.debug( | ||
| f"[_remove_tenant_single_node] Tenant '{tenant}' does not exist. No action taken." | ||
| ) | ||
| return 0 | ||
| if tenant not in node.tenant_to_last_access_time: | ||
| logger.warning( | ||
| f"Tenant '{tenant}' does not have node '{node.text}'. No action taken." | ||
| logger.debug( | ||
| f"[_remove_tenant_single_node] Tenant '{tenant}' does not have node '{node.text}'. No action taken." | ||
| ) | ||
| return 0 | ||
|
|
||
|
|
@@ -239,11 +231,38 @@ def _remove_tenant_single_node(self, tenant: str, node: Node) -> int: | |
|
|
||
| return removed_chars_len | ||
|
|
||
| def add_tenants(self, tenants: List[str], time_s: float) -> None: | ||
| """ | ||
| Add multiple new tenants to the tree. Also inserts an empty string for each tenant into the tree. | ||
|
|
||
| For each tenant that already exists, a warning is logged and that tenant is skipped. | ||
|
|
||
| Args: | ||
| tenants: List of tenants to add | ||
| time_s: Current timestamp in seconds | ||
| """ | ||
| with self.lock: | ||
| for tenant in tenants: | ||
| if tenant in self.tenant_to_char_count: | ||
| logger.debug( | ||
| f"[_add_tenants] Tenant '{tenant}' already exists. Skipping." | ||
| ) | ||
| continue | ||
|
|
||
| self.tenant_to_char_count[tenant] = 0 | ||
| self.tenant_to_lru_tail[tenant] = self.root | ||
|
|
||
| # Initialize the root node as the head of the LRU list for this tenant | ||
| self.root.tenant_to_newer_node[tenant] = None | ||
| self.root.tenant_to_older_node[tenant] = None | ||
| self.insert("", tenant, time_s) | ||
|
|
||
| def insert(self, text: str, tenant: str, time_s: float) -> None: | ||
| """ | ||
| Insert text into tree for a specific tenant. | ||
| Insert text into tree for a specific tenant, but only if the tenant already exists. | ||
|
|
||
| If the tenant doesn't already exist in the tree, it will be automatically added. | ||
| If the tenant doesn't exist in the tree, this will log a warning and return without | ||
| inserting anything. Use add_tenants() first to add a new tenant. | ||
|
|
||
| Args: | ||
| text: Text to insert | ||
|
|
@@ -263,7 +282,10 @@ def insert(self, text: str, tenant: str, time_s: float) -> None: | |
| """ | ||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| self._add_tenant(tenant) | ||
| logger.debug( | ||
| f"[_insert] Tenant '{tenant}' does not exist. Use add_tenants() first." | ||
| ) | ||
| return | ||
|
|
||
| curr_node: Node = self.root | ||
| i: int = 0 | ||
|
|
@@ -373,10 +395,6 @@ def prefix_match( | |
| If the list of available tenants doesn't match any tenants in the tree: returns ("", None) | ||
| When no prefix match is found (does not traverse further than the root node): returns ("", list of available tenants) | ||
| When a prefix match is found: returns (matched_prefix, list of tenants that own the matched node) | ||
|
|
||
| Note: | ||
| A tenant is unable to be returned by prefix_match until it has inserted text into the tree, even if _add_tenant is called. | ||
| The replica scheduler is responsible for inserting text into new replicas; it should not only rely on prefix_match to select replicas. | ||
| """ | ||
| with self.lock: | ||
| if available_tenants: | ||
|
|
@@ -433,38 +451,47 @@ def prefix_match( | |
|
|
||
| return matched_text, matched_tenants | ||
|
|
||
| def remove_tenant(self, tenant: str) -> int: | ||
| def remove_tenants(self, tenants: List[str]) -> Dict[str, int]: | ||
| """ | ||
| Remove a tenant and all its nodes from the tree. | ||
| Time complexity: O(n) where n is the number of nodes owned by the tenant. | ||
| Remove multiple tenants and all their nodes from the tree. | ||
| Time complexity: O(n) where n is the total number of nodes owned by all tenants. | ||
|
|
||
| Args: | ||
| tenant: Tenant to remove | ||
| tenants: List of tenants to remove | ||
|
|
||
| Returns: | ||
| Number of characters removed (0 if tenant doesn't exist) | ||
| Dictionary mapping each tenant to the number of characters removed | ||
| (0 if tenant doesn't exist) | ||
| """ | ||
| chars_removed: Dict[str, int] = {} | ||
|
|
||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| logger.warning(f"Tenant '{tenant}' does not exist. No action taken.") | ||
| return 0 | ||
| for tenant in tenants: | ||
| if tenant not in self.tenant_to_char_count: | ||
| logger.debug( | ||
| f"[_remove_tenants] Tenant '{tenant}' does not exist. Skipping." | ||
| ) | ||
| chars_removed[tenant] = 0 | ||
| continue | ||
|
|
||
| total_chars_removed: int = 0 | ||
| tenant_chars_removed: int = 0 | ||
|
|
||
| # Start from the tail and remove all nodes | ||
| current_tail = self.tenant_to_lru_tail.get(tenant) | ||
| while current_tail: | ||
| newer_neighbor = current_tail.tenant_to_newer_node.get(tenant) | ||
| total_chars_removed += self._remove_tenant_single_node( | ||
| tenant, current_tail | ||
| ) | ||
| current_tail = newer_neighbor | ||
| # Start from the tail and remove all nodes | ||
| current_tail = self.tenant_to_lru_tail.get(tenant) | ||
| while current_tail: | ||
| newer_neighbor = current_tail.tenant_to_newer_node.get(tenant) | ||
| tenant_chars_removed += self._remove_tenant_single_node( | ||
| tenant, current_tail | ||
| ) | ||
| current_tail = newer_neighbor | ||
|
|
||
| # Clean up tenant references | ||
| self.tenant_to_char_count.pop(tenant, None) | ||
| self.tenant_to_lru_tail.pop(tenant, None) | ||
| # Clean up tenant references | ||
| self.tenant_to_char_count.pop(tenant, None) | ||
| self.tenant_to_lru_tail.pop(tenant, None) | ||
|
|
||
| return total_chars_removed | ||
| chars_removed[tenant] = tenant_chars_removed | ||
|
|
||
| return chars_removed | ||
|
|
||
| def evict_tenant_by_lru(self, tenant: str, min_remove_size: int) -> int: | ||
| """ | ||
|
|
@@ -485,14 +512,14 @@ def evict_tenant_by_lru(self, tenant: str, min_remove_size: int) -> int: | |
| """ | ||
| with self.lock: | ||
| if tenant not in self.tenant_to_char_count: | ||
| logger.warning( | ||
| f"Cannot evict tenant '{tenant}': tenant does not exist. No action taken." | ||
| logger.debug( | ||
| f"[_evict_tenant_by_lru] Cannot evict tenant '{tenant}': tenant does not exist. No action taken." | ||
| ) | ||
| return 0 | ||
|
|
||
| if self.tenant_to_char_count[tenant] < min_remove_size: | ||
| logger.warning( | ||
| f"Cannot evict {min_remove_size} characters from tenant '{tenant}', which has only " | ||
| logger.debug( | ||
| f"[_evict_tenant_by_lru] Cannot evict {min_remove_size} characters from tenant '{tenant}', which has only " | ||
| f"{self.tenant_to_char_count[tenant]} characters. Will remove all available characters." | ||
| ) | ||
| min_remove_size = self.tenant_to_char_count[tenant] | ||
|
|
@@ -525,22 +552,65 @@ def evict_tenant_by_lru(self, tenant: str, min_remove_size: int) -> int: | |
|
|
||
| return total_chars_removed | ||
|
|
||
| def get_smallest_tenant(self) -> Optional[str]: | ||
| def get_smallest_tenants(self) -> Optional[List[str]]: | ||
| """ | ||
| Get the tenant with the smallest total character count. | ||
| Get the tenants with the smallest total character count. | ||
|
|
||
| Returns: | ||
| Tenant with smallest character count, or None if no tenants | ||
| Tenants with smallest character count, or None if no tenants | ||
| """ | ||
| with self.lock: | ||
| if not self.tenant_to_char_count: | ||
| return None | ||
|
|
||
| return min( | ||
| self.tenant_to_char_count, | ||
| key=self.tenant_to_char_count.get, | ||
| default=None, | ||
| ) | ||
| min_count = min(self.tenant_to_char_count.values()) | ||
| return [ | ||
| tenant | ||
| for tenant, count in self.tenant_to_char_count.items() | ||
| if count == min_count | ||
| ] | ||
|
|
||
| def start_eviction_loop( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be more like a background thread. (event loop should not be kept busy because of eviction) |
||
| self, eviction_threshold: int, eviction_target: int, interval_secs: float | ||
| ) -> bool: | ||
| """Start a single eviction loop within the actor itself | ||
| Parameters: | ||
eicherseiji marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| eviction_threshold: Minimum number of characters a tenant must have to be evicted | ||
| eviction_target: The maximum number of characters a tenant should have after eviction | ||
| interval_secs: Number of seconds between eviction checks | ||
|
|
||
| Returns: | ||
| True if the loop was started, False if it was already running | ||
| """ | ||
| with self.lock: | ||
| if self._eviction_task is None: | ||
| self._eviction_task = asyncio.create_task( | ||
| self._run_eviction_loop( | ||
| eviction_threshold, eviction_target, interval_secs | ||
| ) | ||
| ) | ||
| return True | ||
| else: | ||
| logger.debug("[_start_eviction_loop] Eviction loop already running") | ||
| return False | ||
|
|
||
| async def _run_eviction_loop( | ||
| self, eviction_threshold, eviction_target, interval_secs | ||
| ): | ||
| while True: | ||
| await asyncio.sleep(interval_secs) | ||
| with self.lock: | ||
| for tenant, char_count in self.tenant_to_char_count.items(): | ||
| if char_count > eviction_threshold: | ||
| excess = char_count - eviction_target | ||
| self.evict_tenant_by_lru(tenant, excess) | ||
|
|
||
| def stop_eviction_loop(self): | ||
| with self.lock: | ||
| if self._eviction_task: | ||
| self._eviction_task.cancel() | ||
| # self._eviction_task.close() | ||
| self._eviction_task = None | ||
|
|
||
|
|
||
| @ray.remote | ||
|
|
@@ -551,3 +621,6 @@ def getattr(self, attribute: str) -> Any: | |
| Note: This method is intended to be used only in tests. | ||
| """ | ||
| return getattr(self, attribute) | ||
|
|
||
| def setattr(self, attribute: str, value: Any) -> None: | ||
| setattr(self, attribute, value) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.