Skip to content

Commit 5f89aff

Browse files
committed
Use separate task to get initial PSRD from hub
1 parent d13be62 commit 5f89aff

5 files changed

Lines changed: 116 additions & 20 deletions

File tree

TODO

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1-
TODO: Add ETSI QKD get-status to system test
1+
TODO: (Re)start get PSRD task when pool runs low
2+
3+
TODO: Remove fully consumed blocks
4+
5+
TODO: Add pool thresholds to configuration (and document this)
26

3-
TODO: Replenish PSRD pool when it runs low
7+
TODO: Make dske.yaml the default configuration file, and add a --config option to change it
8+
9+
TODO: Add test cases for replenishing pools
10+
11+
TODO: Add ETSI QKD get-status to system test
412

513
TODO: Cleanup Shamir's Secret Sharing (SSS) code (no back-and-forth conversion to RawShare)
614

@@ -68,4 +76,4 @@ TODO: Avoid usage: __main__.py in --help output for client / hub
6876

6977
TODO: Add site map to Google Search Console
7078

71-
TODO: Consistently use httpx for client side (not requests for synchronous calls)
79+
TODO: Consistently use httpx for client side (not requests for synchronous calls)

client/http_client.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async def get(
7777
try:
7878
response = await self._httpx_client.get(url, params=params, auth=auth)
7979
except httpx.HTTPError as exc:
80-
LOGGER.error(f"Call GET {url} exception {str(exc)}")
80+
LOGGER.error(f"Call GET {exc.request.url} exception {str(exc)}")
8181
raise exceptions.HTTPError(
8282
method="GET",
8383
url=url,
@@ -86,7 +86,7 @@ async def get(
8686
exception=str(exc),
8787
) from exc
8888
if response.status_code != 200:
89-
LOGGER.error(f"Call GET {url} {response.status_code}")
89+
LOGGER.error(f"Call GET {response.request.url} {response.status_code}")
9090
raise exceptions.HTTPError(
9191
method="GET",
9292
url=url,
@@ -95,7 +95,7 @@ async def get(
9595
status_code=response.status_code,
9696
response=response.content,
9797
)
98-
LOGGER.info(f"Call GET {url} {response.status_code}")
98+
LOGGER.info(f"Call GET {response.request.url} {response.status_code}")
9999
if api_response_class is None:
100100
return None
101101
try:

client/peer_hub.py

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,29 @@
1919
from common.utils import bytes_to_str, str_to_bytes
2020
from .http_client import HttpClient
2121

22-
# TODO: Decide on logic on how the PSRD block size is decided. Does the client decide? Does
23-
# the hub decide?
24-
_PSRD_BLOCK_SIZE_IN_BYTES = 1000
25-
_FAILED_REQUEST_RETRY_DELAY_IN_SECONDS = 1.0
22+
# TODO: Make the following configurable.
23+
24+
_START_REQUEST_PSRD_THRESHOLD = 500
25+
"""
26+
Start requesting more PSRD blocks from the hub when the amount of PSRD in the pool falls below this
27+
threshold.
28+
"""
29+
30+
_STOP_REQUEST_PSRD_THRESHOLD = 2000
31+
"""
32+
Stop requesting more PSRD blocks from the hub when the amount of PSRD in the pool rises above or
33+
equal to this threshold.
34+
"""
35+
36+
_GET_PSRD_BLOCK_SIZE = 2000
37+
"""
38+
When requesting more PSRD blocks from the hub, request blocks of this size.
39+
"""
40+
41+
_GET_PSRD_RETRY_DELAY = 1.0
42+
"""
43+
If a get PSRD request to the hub fails, wait this many seconds before retrying.
44+
"""
2645

2746

2847
class PeerHub:
@@ -34,6 +53,7 @@ class PeerHub:
3453
_http_client: HttpClient
3554
_base_url: str
3655
_startup_task: asyncio.Task | None = None
56+
_request_psrd_task: asyncio.Task | None = None
3757
_registered: bool
3858
_local_pool: Pool
3959
_peer_pool: Pool
@@ -45,6 +65,8 @@ def __init__(self, client, base_url):
4565
self._base_url = base_url
4666
if self._base_url.endswith("/"):
4767
self._base_url = self._base_url[:-1]
68+
self._startup_task = None
69+
self._request_psrd_task = None
4870
self._registered = False
4971
self._local_pool = Pool(Pool.Owner.LOCAL)
5072
self._peer_pool = Pool(Pool.Owner.PEER)
@@ -82,7 +104,6 @@ def start(self) -> None:
82104
"""
83105
assert self._startup_task is None
84106
self._startup_task = asyncio.create_task(self.start_task())
85-
# TODO: Do we need a done_callback? Set start_task to None?
86107

87108
async def start_task(self) -> None:
88109
"""
@@ -91,16 +112,17 @@ async def start_task(self) -> None:
91112
- Request the initial block of Pre-Shared Random Data (PSRD) from the hub (also periodically
92113
retrying if it fails).
93114
"""
115+
LOGGER.info(f"Begin start task for peer hub at {self._base_url}")
94116
try:
95117
while not await self.attempt_registration():
96-
await asyncio.sleep(_FAILED_REQUEST_RETRY_DELAY_IN_SECONDS)
97-
for owner in (Pool.Owner.LOCAL, Pool.Owner.PEER):
98-
while not await self.attempt_request_psrd(owner):
99-
await asyncio.sleep(_FAILED_REQUEST_RETRY_DELAY_IN_SECONDS)
118+
await asyncio.sleep(_GET_PSRD_RETRY_DELAY)
100119
except asyncio.CancelledError:
101-
# The task is cancelled when the client is shut down before startup is complete.
102-
# TODO: Do we need to do anything here? The un-registration is done elsewhere
103-
pass
120+
self._startup_task = None
121+
LOGGER.info(f"Cancel start task for peer hub at {self._base_url}")
122+
return
123+
self.start_request_psrd_task_if_needed()
124+
self._startup_task = None
125+
LOGGER.info(f"Finish start task for peer hub at {self._base_url}")
104126

105127
async def attempt_registration(self) -> bool:
106128
"""
@@ -125,7 +147,59 @@ async def unregister(self) -> None:
125147
"""
126148
Unregister this client from the peer hub.
127149
"""
128-
# TODO: Implement this
150+
# TODO: Implement this and call it from somewhere
151+
152+
def start_request_psrd_task_if_needed(self) -> None:
153+
"""
154+
Start the request PSRD task if needed.
155+
"""
156+
if self._request_psrd_task is None:
157+
if self.at_least_one_pool_below_start_threshold():
158+
self._request_psrd_task = asyncio.create_task(self.request_psrd_task())
159+
160+
def at_least_one_pool_below_start_threshold(self) -> bool:
161+
"""
162+
Check if the number of bytes available at least one of the pools is below the threshold
163+
for starting to get more PSRD from the hub.
164+
"""
165+
return (
166+
self._local_pool.bytes_available < _START_REQUEST_PSRD_THRESHOLD
167+
or self._peer_pool.bytes_available < _START_REQUEST_PSRD_THRESHOLD
168+
)
169+
170+
def all_pools_above_stop_threshold(self) -> bool:
171+
"""
172+
Check if the number of bytes available in all of the pools is above the threshold for
173+
stopping to get more PSRD from the hub.
174+
"""
175+
return (
176+
self._local_pool.bytes_available >= _STOP_REQUEST_PSRD_THRESHOLD
177+
and self._peer_pool.bytes_available >= _STOP_REQUEST_PSRD_THRESHOLD
178+
)
179+
180+
async def request_psrd_task(self) -> None:
181+
"""
182+
Task for requesting Pre-Shared Random Data (PSRD) from the peer hub. This tasks runs as long
183+
as the local pool or the peer pool need more PSRD.
184+
"""
185+
LOGGER.info(f"Begin request PSRD task for peer hub {self._hub_name}")
186+
try:
187+
while not self.all_pools_above_stop_threshold():
188+
need_delay = False
189+
if self._local_pool.bytes_available < _STOP_REQUEST_PSRD_THRESHOLD:
190+
if not await self.attempt_request_psrd(Pool.Owner.LOCAL):
191+
need_delay = True
192+
if self._peer_pool.bytes_available < _STOP_REQUEST_PSRD_THRESHOLD:
193+
if not await self.attempt_request_psrd(Pool.Owner.PEER):
194+
need_delay = True
195+
if need_delay:
196+
await asyncio.sleep(_GET_PSRD_RETRY_DELAY)
197+
except asyncio.CancelledError:
198+
self._request_psrd_task = None
199+
LOGGER.info(f"Cancel request PSRD task for peer hub {self._hub_name}")
200+
return
201+
self._request_psrd_task = None
202+
LOGGER.info(f"Finish request PSRD task for peer hub {self._hub_name}")
129203

130204
async def attempt_request_psrd(self, owner: Pool.Owner) -> bool:
131205
"""
@@ -144,7 +218,7 @@ async def attempt_request_psrd(self, owner: Pool.Owner) -> bool:
144218
params = {
145219
"client_name": self._client.name,
146220
"pool_owner": pool_owner_str,
147-
"size": _PSRD_BLOCK_SIZE_IN_BYTES,
221+
"size": _GET_PSRD_BLOCK_SIZE,
148222
}
149223
try:
150224
api_block = await self._http_client.get(url, params, APIBlock)

common/block.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ def uuid(self):
4545
"""
4646
return self._block_uuid
4747

48+
@property
49+
def bytes_available_for_allocation(self):
50+
"""
51+
Return the number of bytes available for allocation in the block.
52+
"""
53+
return self._size - self._allocated.count()
54+
4855
def to_mgmt(self):
4956
"""
5057
Get the management status.

common/pool.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ def owner(self) -> Owner:
4848
"""
4949
return self._owner
5050

51+
@property
52+
def bytes_available(self):
53+
"""
54+
Return the number of bytes available for allocation in the pool.
55+
"""
56+
return sum(block.bytes_available_for_allocation for block in self._blocks)
57+
5158
def to_mgmt(self) -> dict:
5259
"""
5360
Get the management status.

0 commit comments

Comments
 (0)