Skip to content

Commit

Permalink
refactor: re-implement the OTA progress stats collector (#352)
Browse files Browse the repository at this point in the history
This PR re-implements the OTA progress stats collector implementation as it is out-of-date considering the recent changes to otaclient module. The new implementation is simpler and fits the current otaclient module.

Other changes:
1. otaclient: use DownloaderPool's watchdog func instead of OTAUpdater's one.
2. fix tests cannot exit cleanly on failure due to background OTA image server process doesn't exit.
3. OTAUpdater implements a minimum 1s interval for status query and implements the cache of status response. Query frequent than min interval will be responded with cached status response.
  • Loading branch information
Bodong-Yang authored Jul 8, 2024
1 parent 5c547dc commit 85ae396
Show file tree
Hide file tree
Showing 7 changed files with 328 additions and 412 deletions.
17 changes: 6 additions & 11 deletions src/otaclient/app/create_standby/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@
from otaclient_common.common import create_tmp_fname

from ..configs import config as cfg
from ..update_stats import (
OTAUpdateStatsCollector,
RegInfProcessedStats,
RegProcessOperation,
)
from ..update_stats import OperationRecord, OTAUpdateStatsCollector, ProcessOperation

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -286,7 +282,6 @@ def _prepare_local_copy_from_active_slot(
except KeyError:
pass

start_time = time.thread_time_ns()
tmp_f = self._local_copy_dir / create_tmp_fname()
hash_buffer, hash_bufferview = thread_local.buffer, thread_local.view
try:
Expand All @@ -311,11 +306,11 @@ def _prepare_local_copy_from_active_slot(
tmp_f.unlink(missing_ok=True)

# report to the ota update stats collector
self._stats_collector.report_prepare_local_copy(
RegInfProcessedStats(
op=RegProcessOperation.PREPARE_LOCAL_COPY,
size=fpath.stat().st_size,
elapsed_ns=time.thread_time_ns() - start_time,
self._stats_collector.report_stat(
OperationRecord(
op=ProcessOperation.PREPARE_LOCAL_COPY,
processed_file_size=fpath.stat().st_size,
processed_file_num=1,
),
)

Expand Down
28 changes: 10 additions & 18 deletions src/otaclient/app/create_standby/rebuild_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
import logging
import os
import shutil
import time
from pathlib import Path
from typing import List, Set, Tuple
from typing import Set, Tuple

from ota_metadata.legacy.parser import MetafilesV1, OTAMetadata
from ota_metadata.legacy.types import RegularInf
Expand All @@ -28,11 +27,7 @@
)

from ..configs import config as cfg
from ..update_stats import (
OTAUpdateStatsCollector,
RegInfProcessedStats,
RegProcessOperation,
)
from ..update_stats import OperationRecord, OTAUpdateStatsCollector, ProcessOperation
from .common import DeltaBundle, DeltaGenerator, HardlinkRegister
from .interface import StandbySlotCreatorProtocol

Expand Down Expand Up @@ -111,20 +106,17 @@ def _task(_item):
except TasksEnsureFailed as e:
logger.error(f"failed to finish up file processing: {e!r}")
raise
self.stats_collector.wait_staging()

def _process_regular(self, _input: Tuple[bytes, Set[RegularInf]]):
_hash, _regs_set = _input
_hash_str = _hash.hex()
stats_list: List[RegInfProcessedStats] = [] # for ota stats report
stat_report = OperationRecord(op=ProcessOperation.APPLY_DELTA)

_local_copy = self._ota_tmp / _hash_str
_f_size = _local_copy.stat().st_size
for _count, entry in enumerate(_regs_set, start=1):
is_last = _count == len(_regs_set)

_start_time = time.thread_time_ns()

# special treatment on /boot folder
_mount_point = (
self.standby_slot_mp
Expand Down Expand Up @@ -161,14 +153,14 @@ def _process_regular(self, _input: Tuple[bytes, Set[RegularInf]]):
if is_last:
_local_copy.unlink(missing_ok=True)

cur_stat = RegInfProcessedStats(
op=RegProcessOperation.APPLY_DELTA,
size=_f_size,
elapsed_ns=time.thread_time_ns() - _start_time,
)
stats_list.append(cur_stat)
# NOTE(20240704): the first copy of the file is either prepared by local delta copy
# or downloading from remote, so unconditionally pop one record away.
if not is_last:
stat_report.processed_file_num += 1
stat_report.processed_file_size += _f_size

# report the stats to the stats_collector
self.stats_collector.report_apply_delta(stats_list)
self.stats_collector.report_stat(stat_report)

def _save_meta(self):
"""Save metadata to META_FOLDER."""
Expand Down
Loading

1 comment on commit 85ae396

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/ota_metadata/legacy
   __init__.py110100% 
   parser.py3353888%100, 156, 161, 197–198, 208–209, 212, 224, 282, 292–295, 334–337, 417, 420, 428–430, 443, 452–453, 456–457, 669–670, 673, 700–702, 752, 755–757
   types.py841384%37, 40–42, 112–116, 122–125
src/ota_proxy
   __init__.py361072%59, 61, 63, 72, 81–82, 102, 104–106
   __main__.py770%16–18, 20, 22–23, 25
   _consts.py150100% 
   cache_control.py68494%71, 91, 113, 121
   config.py180100% 
   db.py1461589%75, 81, 103, 113, 116, 145–147, 166, 199, 208–209, 229, 258, 300
   errors.py50100% 
   orm.py1121091%92, 97, 102, 108, 114, 141–142, 155, 232, 236
   ota_cache.py4019676%98–99, 218, 229, 238, 241–242, 256–258, 278, 294–295, 297, 320–321, 327, 331, 333, 359–362, 364–366, 378, 439–440, 482–483, 553, 558–560, 566–569, 619, 638–639, 671–672, 683, 717–721, 725–727, 729, 731–738, 740–742, 745–746, 750–751, 755, 802, 810–812, 891–894, 898, 901–902, 916–917, 919–921, 925–926, 932–933, 964, 970, 997, 1026–1028
   server_app.py1383971%76, 79, 85, 101, 103, 162, 171, 213–214, 216–218, 221, 226–228, 231–232, 235, 238, 241, 244, 257–258, 261–262, 264, 267, 293–296, 299, 313–315, 321–323
   utils.py23195%33
src/otaclient
   __init__.py5260%17, 19
   __main__.py110%16
   log_setting.py52590%53, 55, 64–66
src/otaclient/app
   __main__.py110%16
   configs.py760100% 
   errors.py1200100% 
   interface.py50100% 
   main.py46589%52–53, 75–77
   ota_client.py39312368%73, 81, 102, 129, 131–132, 134, 138, 142–143, 148–149, 155, 157, 195–198, 204, 208, 214, 317, 360–363, 379–384, 397–400, 406–407, 409, 418, 421, 426–427, 430, 436, 438, 441, 473–476, 479–486, 494–497, 527–528, 532, 534–535, 565–566, 575–582, 589, 592–598, 645–648, 656, 693, 698–701, 706–708, 711–712, 714–715, 717–718, 720, 780–781, 784, 792–793, 796, 807–808, 811, 819–820, 823, 834, 853, 880, 899, 917
   ota_client_stub.py39410972%76–78, 80–81, 89–92, 95–97, 101, 106–107, 109–110, 113, 115–116, 119–121, 124–125, 128–130, 135–140, 144, 147–151, 153–154, 162–164, 167, 204–206, 211, 247, 272, 275, 278, 382, 406, 408, 432, 478, 535, 605–606, 645, 664–666, 672–675, 679–681, 688–690, 693, 697–700, 753, 842–844, 851, 881–882, 885–889, 898–907, 914, 920, 923–924, 928, 931
   update_stats.py100892%89, 91, 100, 102, 108, 110, 130, 161
src/otaclient/app/boot_control
   __init__.py40100% 
   _common.py24811254%74–75, 96–98, 114–115, 135–136, 155–156, 175–176, 195–196, 218–220, 235–236, 260–266, 287, 295, 313, 321, 340–341, 344–345, 368, 370–379, 381–390, 392–394, 413, 416, 424, 432, 448–450, 452–457, 550, 555, 560, 673, 677–678, 681, 689, 691–692, 718–719, 721–724, 729, 735–736, 739–740, 742, 749–750, 761–767, 775–777, 781–782, 785–786, 789, 795
   _grub.py41712869%216, 264–267, 273–277, 314–315, 322–327, 330–336, 339, 342–343, 348, 350–352, 361–367, 369–370, 372–374, 383–385, 387–389, 468–469, 473–474, 526, 532, 558, 580, 584–585, 600–602, 626–629, 641, 645–647, 649–651, 710–713, 738–741, 764–767, 779–780, 783–784, 819, 825, 845–846, 848, 860, 863, 866, 869, 873–875, 893–896, 924–927, 932–940, 945–953
   _jetson_cboot.py27021420%69–70, 77–78, 96–105, 117, 124–125, 137, 143–144, 154–156, 168–169, 180–181, 184–185, 188–189, 192–196, 199–200, 204–205, 210–211, 213–217, 219–225, 227–228, 233, 236, 239–240, 243, 247–248, 252–253, 257, 260, 263, 267–273, 275–277, 282, 285, 288, 292, 299, 301–304, 317, 320, 324, 326–328, 332, 339, 341, 344, 350–351, 356, 364, 372–374, 383–384, 386–388, 394, 397–399, 403–404, 406, 409, 418–420, 423, 426, 429–434, 436–438, 441, 444, 448–453, 457–459, 464–465, 469–470, 473, 476, 479–480, 483, 486, 491, 494, 497–498, 500, 502, 505, 508, 510–511, 514–518, 523–524, 526, 534–538, 540, 543, 546, 557–558, 563, 573, 576–584, 589–597, 602–610, 616–618, 621, 624
   _jetson_common.py1416653%50, 74, 129–134, 136, 141–143, 148–151, 159–160, 167–168, 173–174, 190–191, 193–195, 198–200, 203, 207, 211, 215–217, 223–224, 226, 259, 285–286, 288–290, 294–297, 299–300, 302–306, 308, 315–316, 319, 321, 331, 334–335, 338, 340
   _rpi_boot.py28613453%54, 57, 121–122, 126, 134–137, 151–154, 161–162, 164–165, 170–171, 174–175, 184–185, 223, 229–233, 236, 254–256, 260–262, 267–269, 273–275, 285–286, 289, 292, 294–295, 297–298, 300–302, 308, 311–312, 322–325, 333–337, 339, 341–342, 347–348, 355–361, 392, 394–397, 407–410, 414–415, 417–421, 449–452, 471–474, 479, 482, 500–503, 508–516, 521–529, 546–549, 555–557, 560, 563
   configs.py380100% 
   protocol.py40100% 
   selecter.py382631%44–46, 49–50, 54–55, 58–60, 63, 65, 69, 77–79, 81–82, 84–85, 89, 91–93, 95, 97
src/otaclient/app/create_standby
   __init__.py12558%28–30, 32, 34
   common.py2244480%59, 62–63, 67–69, 71, 75–76, 78, 124, 172–174, 176–178, 180, 183–186, 190, 201, 275–276, 278–283, 295, 332, 360, 363–365, 381–382, 396, 400, 422–423
   interface.py50100% 
   rebuild_mode.py97990%89–91, 103–108
src/otaclient/configs
   _common.py80100% 
   ecu_info.py57198%107
   proxy_info.py52296%88, 90
src/otaclient_api/v2
   __init__.py140100% 
   api_caller.py39684%45–47, 83–85
   api_stub.py170100% 
   types.py2562391%86, 89–92, 131, 209–210, 212, 259, 262–263, 506–508, 512–513, 515, 518–519, 522–523, 586
src/otaclient_common
   __init__.py34876%42–44, 59, 61, 67, 74–75
   common.py1541987%41, 45, 200, 203–205, 220, 227–229, 295–297, 307, 316–318, 364, 368
   downloader.py2001095%110–111, 129, 156, 372, 426, 430, 518–519, 528
   linux.py611575%51–53, 59, 69, 74, 76, 108–109, 133–134, 190, 195–196, 198
   logging.py29196%55
   persist_file_handling.py1131884%112, 114, 146–148, 150, 176–179, 184, 188–192, 218–219
   proto_streamer.py42880%33, 48, 66–67, 72, 81–82, 100
   proto_wrapper.py3984887%87, 165, 172, 184–186, 205, 210, 221, 257, 263, 268, 299, 303, 307, 402, 462, 469, 472, 492, 499, 501, 526, 532, 535, 537, 562, 568, 571, 573, 605, 609, 611, 625, 642, 669, 672, 676, 707, 713, 760–763, 765, 803–805
   retry_task_map.py80791%164–165, 167, 179–182
   typing.py25388%69–70, 72
TOTAL5955139476% 

Tests Skipped Failures Errors Time
174 0 💤 0 ❌ 0 🔥 5m 24s ⏱️

Please sign in to comment.