Skip to content

Commit a643bfb

Browse files
authored
Merge pull request #263 from input-output-hk/add_wait_for_epoch
feat(waiting): add `wait_for_epoch` function
2 parents 987b814 + d093484 commit a643bfb

File tree

2 files changed

+115
-48
lines changed

2 files changed

+115
-48
lines changed

cardano_clusterlib/clusterlib_helpers.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,93 @@ def wait_for_block(
254254

255255
LOGGER.debug(f"New block(s) were created; block number: {this_block}")
256256
return this_block
257+
258+
259+
def poll_new_epoch(
260+
clusterlib_obj: "itp.ClusterLib",
261+
exp_epoch: int,
262+
padding_seconds: int = 0,
263+
) -> None:
264+
"""Wait for new epoch(s) by polling current epoch every 3 sec.
265+
266+
Can be used only for waiting up to 3000 sec + padding seconds.
267+
268+
Args:
269+
clusterlib_obj: An instance of `ClusterLib`.
270+
tip: Current tip - last block successfully applied to the ledger.
271+
exp_epoch: An epoch number to wait for.
272+
padding_seconds: A number of additional seconds to wait for (optional).
273+
"""
274+
for check_no in range(1000):
275+
wakeup_epoch = clusterlib_obj.g_query.get_epoch()
276+
if wakeup_epoch != exp_epoch:
277+
time.sleep(3)
278+
continue
279+
# We are in the expected epoch right from the beginning, we'll skip padding seconds
280+
if check_no == 0:
281+
break
282+
if padding_seconds:
283+
time.sleep(padding_seconds)
284+
break
285+
286+
287+
def wait_for_epoch(
288+
clusterlib_obj: "itp.ClusterLib",
289+
tip: tp.Dict[str, tp.Any],
290+
epoch_no: int,
291+
padding_seconds: int = 0,
292+
future_is_ok: bool = True,
293+
) -> int:
294+
"""Wait for epoch no.
295+
296+
Args:
297+
clusterlib_obj: An instance of `ClusterLib`.
298+
tip: Current tip - last block successfully applied to the ledger.
299+
epoch_no: A number of epoch to wait for.
300+
padding_seconds: A number of additional seconds to wait for (optional).
301+
future_is_ok: A bool indicating whether current epoch > `epoch_no` is acceptable
302+
(default: True).
303+
304+
Returns:
305+
int: The current epoch.
306+
"""
307+
start_epoch = int(tip["epoch"])
308+
309+
if epoch_no < start_epoch:
310+
if not future_is_ok:
311+
msg = f"Current epoch is {start_epoch}. The requested epoch {epoch_no} is in the past."
312+
raise exceptions.CLIError(msg)
313+
return start_epoch
314+
315+
LOGGER.debug(f"Current epoch: {start_epoch}; Waiting for the beginning of epoch: {epoch_no}")
316+
317+
new_epochs = epoch_no - start_epoch
318+
319+
# Calculate and wait for the expected slot
320+
boundary_slot = int(
321+
(start_epoch + new_epochs) * clusterlib_obj.epoch_length - clusterlib_obj.slots_offset
322+
)
323+
padding_slots = int(padding_seconds / clusterlib_obj.slot_length) if padding_seconds else 5
324+
exp_slot = boundary_slot + padding_slots
325+
clusterlib_obj.wait_for_slot(slot=exp_slot)
326+
327+
this_epoch = clusterlib_obj.g_query.get_epoch()
328+
if this_epoch != epoch_no:
329+
LOGGER.error(
330+
f"Waited for epoch number {epoch_no} and current epoch is "
331+
f"number {this_epoch}, wrong `slots_offset` ({clusterlib_obj.slots_offset})?"
332+
)
333+
# attempt to get the epoch boundary as precisely as possible failed, now just
334+
# query epoch number and wait
335+
poll_new_epoch(
336+
clusterlib_obj=clusterlib_obj, exp_epoch=epoch_no, padding_seconds=padding_seconds
337+
)
338+
339+
# Still not in the correct epoch? Something is wrong.
340+
this_epoch = clusterlib_obj.g_query.get_epoch()
341+
if this_epoch != epoch_no:
342+
msg = f"Waited for epoch number {epoch_no} and current epoch is number {this_epoch}."
343+
raise exceptions.CLIError(msg)
344+
345+
LOGGER.debug(f"Expected epoch started; epoch number: {this_epoch}")
346+
return this_epoch

cardano_clusterlib/clusterlib_klass.py

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -379,27 +379,6 @@ def wait_for_slot(self, slot: int) -> int:
379379
msg = f"Failed to wait for slot number {slot}."
380380
raise exceptions.CLIError(msg)
381381

382-
def poll_new_epoch(self, exp_epoch: int, padding_seconds: int = 0) -> None:
383-
"""Wait for new epoch(s) by polling current epoch every 3 sec.
384-
385-
Can be used only for waiting up to 3000 sec + padding seconds.
386-
387-
Args:
388-
exp_epoch: An epoch number to wait for.
389-
padding_seconds: A number of additional seconds to wait for (optional).
390-
"""
391-
for check_no in range(1000):
392-
wakeup_epoch = self.g_query.get_epoch()
393-
if wakeup_epoch != exp_epoch:
394-
time.sleep(3)
395-
continue
396-
# we are in the expected epoch right from the beginning, we'll skip padding seconds
397-
if check_no == 0:
398-
break
399-
if padding_seconds:
400-
time.sleep(padding_seconds)
401-
break
402-
403382
def wait_for_new_epoch(self, new_epochs: int = 1, padding_seconds: int = 0) -> int:
404383
"""Wait for new epoch(s).
405384
@@ -410,40 +389,38 @@ def wait_for_new_epoch(self, new_epochs: int = 1, padding_seconds: int = 0) -> i
410389
Returns:
411390
int: The current epoch.
412391
"""
413-
start_epoch = self.g_query.get_epoch()
392+
start_tip = self.g_query.get_tip()
393+
start_epoch = int(start_tip["epoch"])
414394

415395
if new_epochs < 1:
416396
return start_epoch
417397

418-
exp_epoch = start_epoch + new_epochs
419-
LOGGER.debug(
420-
f"Current epoch: {start_epoch}; Waiting for the beginning of epoch: {exp_epoch}"
398+
epoch_no = start_epoch + new_epochs
399+
return clusterlib_helpers.wait_for_epoch(
400+
clusterlib_obj=self, tip=start_tip, epoch_no=epoch_no, padding_seconds=padding_seconds
421401
)
422402

423-
# calculate and wait for the expected slot
424-
boundary_slot = int((start_epoch + new_epochs) * self.epoch_length - self.slots_offset)
425-
padding_slots = int(padding_seconds / self.slot_length) if padding_seconds else 5
426-
exp_slot = boundary_slot + padding_slots
427-
self.wait_for_slot(slot=exp_slot)
428-
429-
this_epoch = self.g_query.get_epoch()
430-
if this_epoch != exp_epoch:
431-
LOGGER.error(
432-
f"Waited for epoch number {exp_epoch} and current epoch is "
433-
f"number {this_epoch}, wrong `slots_offset` ({self.slots_offset})?"
434-
)
435-
# attempt to get the epoch boundary as precisely as possible failed, now just
436-
# query epoch number and wait
437-
self.poll_new_epoch(exp_epoch=exp_epoch, padding_seconds=padding_seconds)
438-
439-
# Still not in the correct epoch? Something is wrong.
440-
this_epoch = self.g_query.get_epoch()
441-
if this_epoch != exp_epoch:
442-
msg = f"Waited for epoch number {exp_epoch} and current epoch is number {this_epoch}."
443-
raise exceptions.CLIError(msg)
403+
def wait_for_epoch(
404+
self, epoch_no: int, padding_seconds: int = 0, future_is_ok: bool = True
405+
) -> int:
406+
"""Wait for epoch no.
407+
408+
Args:
409+
epoch_no: A number of epoch to wait for.
410+
padding_seconds: A number of additional seconds to wait for (optional).
411+
future_is_ok: A bool indicating whether current epoch > `epoch_no` is acceptable
412+
(default: True).
444413
445-
LOGGER.debug(f"Expected epoch started; epoch number: {this_epoch}")
446-
return this_epoch
414+
Returns:
415+
int: The current epoch.
416+
"""
417+
return clusterlib_helpers.wait_for_epoch(
418+
clusterlib_obj=self,
419+
tip=self.g_query.get_tip(),
420+
epoch_no=epoch_no,
421+
padding_seconds=padding_seconds,
422+
future_is_ok=future_is_ok,
423+
)
447424

448425
def time_to_epoch_end(self, tip: tp.Optional[dict] = None) -> float:
449426
"""How many seconds to go to start of a new epoch."""

0 commit comments

Comments
 (0)