|
8 | 8 | import logging |
9 | 9 | from tornado.web import HTTPError |
10 | 10 | from traitlets.config import LoggingConfigurable |
11 | | -from traitlets import Float |
| 11 | +from traitlets import Float, validate |
12 | 12 |
|
13 | 13 | if TYPE_CHECKING: |
14 | 14 | from typing import Any, Coroutine, Literal |
|
17 | 17 | from jupyter_server.services.contents.manager import ContentsManager |
18 | 18 | from ..outputs.manager import OutputsManager |
19 | 19 |
|
| 20 | +DEFAULT_MIN_POLL_INTERVAL = 0.5 |
| 21 | +DEFAULT_POLL_INTERVAL_MULTIPLIER = 5.0 |
20 | 22 | class YRoomFileAPI(LoggingConfigurable): |
21 | 23 | """Provides an API to interact with a single file for a YRoom. |
22 | 24 |
|
@@ -45,23 +47,15 @@ class YRoomFileAPI(LoggingConfigurable): |
45 | 47 | file_id: The unique identifier for this file. |
46 | 48 | """ |
47 | 49 |
|
48 | | - poll_interval = Float( |
49 | | - default_value=0.5, |
50 | | - help="Sets the initial interval for saving the YDoc & checking the file " |
51 | | - "for changes. This serves as the starting value before adaptive timing " |
52 | | - "takes effect. Defaults to 0.5 seconds.", |
53 | | - config=True, |
54 | | - ) |
55 | | - |
56 | 50 | min_poll_interval = Float( |
57 | | - default_value=0.5, |
| 51 | + default_value=DEFAULT_MIN_POLL_INTERVAL, |
58 | 52 | help="Minimum autosave interval in seconds. The adaptive timing will " |
59 | 53 | "never go below this value. Defaults to 0.5 seconds.", |
60 | 54 | config=True, |
61 | 55 | ) |
62 | 56 |
|
63 | 57 | poll_interval_multiplier = Float( |
64 | | - default_value=5.0, |
| 58 | + default_value=DEFAULT_POLL_INTERVAL_MULTIPLIER, |
65 | 59 | help="Multiplier applied to save duration to calculate the next poll " |
66 | 60 | "interval. For example, if a save takes 1 second and the multiplier is " |
67 | 61 | "5.0, the next poll interval will be 5 seconds (bounded by min/max). " |
@@ -126,12 +120,6 @@ class YRoomFileAPI(LoggingConfigurable): |
126 | 120 | dual-writes or dirty-reads. |
127 | 121 | """ |
128 | 122 |
|
129 | | - _last_save_duration: float | None |
130 | | - """ |
131 | | - The duration in seconds of the last save operation. Used to calculate the |
132 | | - adaptive poll interval. |
133 | | - """ |
134 | | - |
135 | 123 | _adaptive_poll_interval: float |
136 | 124 | """ |
137 | 125 | The current adaptive poll interval in seconds, calculated based on the last |
@@ -165,8 +153,26 @@ def __init__(self, *args, **kwargs): |
165 | 153 | self._content_lock = asyncio.Lock() |
166 | 154 |
|
167 | 155 | # Initialize adaptive timing attributes |
168 | | - self._last_save_duration = None |
169 | | - self._adaptive_poll_interval = self.poll_interval |
| 156 | + self._adaptive_poll_interval = self.min_poll_interval |
| 157 | + |
| 158 | + @validate("min_poll_interval", "poll_interval_multiplier") |
| 159 | + def _validate_adaptive_timing_traits(self, proposal): |
| 160 | + trait_name = proposal['trait'].name |
| 161 | + value = proposal['value'] |
| 162 | + |
| 163 | + if trait_name == "min_poll_interval": |
| 164 | + default_value = DEFAULT_MIN_POLL_INTERVAL |
| 165 | + else: |
| 166 | + default_value = DEFAULT_POLL_INTERVAL_MULTIPLIER |
| 167 | + |
| 168 | + if value <= 0: |
| 169 | + self.log.warning( |
| 170 | + f"`YRoomFileAPI.{trait_name}` must be >0. Received: {value}. " |
| 171 | + f"Reverting to default value {default_value}." |
| 172 | + ) |
| 173 | + return default_value |
| 174 | + |
| 175 | + return proposal["value"] |
170 | 176 |
|
171 | 177 |
|
172 | 178 | def get_path(self) -> str | None: |
@@ -547,9 +553,8 @@ async def save(self, jupyter_ydoc: YBaseDoc): |
547 | 553 | # JupyterLab tab for this YDoc in the frontend. |
548 | 554 | jupyter_ydoc.dirty = False |
549 | 555 |
|
550 | | - # Calculate save duration and update adaptive poll interval |
| 556 | + # Calculate save duration |
551 | 557 | save_duration = time.perf_counter() - start_time |
552 | | - self._last_save_duration = save_duration |
553 | 558 |
|
554 | 559 | # Calculate new adaptive interval |
555 | 560 | old_interval = self._adaptive_poll_interval |
@@ -619,8 +624,7 @@ def restart(self) -> None: |
619 | 624 | self._last_path = None |
620 | 625 |
|
621 | 626 | # Reset adaptive timing attributes |
622 | | - self._last_save_duration = None |
623 | | - self._adaptive_poll_interval = self.poll_interval |
| 627 | + self._adaptive_poll_interval = self.min_poll_interval |
624 | 628 |
|
625 | 629 | self.log.info(f"Restarted FileAPI for room '{self.room_id}'.") |
626 | 630 |
|
|
0 commit comments