Skip to content
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

perf: improve bootstrap performance hash bins #655

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 43 additions & 14 deletions repository_service_tuf_worker/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#
# SPDX-License-Identifier: MIT

import concurrent.futures
import copy
import enum
import logging
Expand All @@ -23,6 +24,7 @@
KEY_FOR_TYPE_AND_SCHEME,
Key,
Signature,
Signer,
SigstoreKey,
)
from tuf.api.exceptions import (
Expand Down Expand Up @@ -238,15 +240,14 @@
settings_data[key] = value
redis_loader.write(self._settings, settings_data)

def _sign(self, role: Metadata) -> None:
def _sign(self, role: Metadata, signer: Optional[Signer] = None) -> None:
"""
Re-signs metadata with role-specific key from global key store.

The metadata role type is used as default key id. This is only allowed
for top-level roles.
"""
signer = self._signer_store.get(self._online_key)
role.sign(signer)
role.sign(signer or self._signer_store.get(self._online_key))

def _persist(self, role: Metadata, role_name: str) -> str:
"""
Expand Down Expand Up @@ -274,15 +275,18 @@
logging.debug(f"{filename} saved")
return filename

def _bump_expiry(self, role: Metadata, role_name: str) -> None:
def _bump_expiry(
self, role: Metadata, role_name: str, expire: Optional[int] = None
) -> None:
"""
Bumps metadata expiration date by role-specific interval.
"""
role.signed.expires = datetime.now(timezone.utc).replace(
microsecond=0
) + timedelta(
days=int(
self._settings.get_fresh(f"{role_name.upper()}_EXPIRATION")
expire
or self._settings.get_fresh(f"{role_name.upper()}_EXPIRATION")
)
)

Expand Down Expand Up @@ -675,13 +679,11 @@
def _add_metadata_hashbin_delegations(
self,
targets: Metadata[Targets],
snapshot: Metadata[Snapshot],
):
"""Setup target delegations no matter if succinct hash bin or custom"""
delegated_roles: List[str] = []

# Using succinct hash bin delegations.
# Calculate the bit length (Number of bits between 1 and 32)
# Calculate the bit length (Number of bits between 1 and 32)
bit_length = int(
log(self._settings.get_fresh("NUMBER_OF_DELEGATED_BINS"), 2)
)
Expand All @@ -695,20 +697,47 @@
# service.
db_target_roles: List[targets_schema.RSTUFTargetRoleCreate] = []

for delegated_name in succinct_roles.get_roles():
targets.signed.add_key(self._online_key, delegated_name)
# Performance improvement: Use threads to create all delegated roles
#
# 1. During bootstrap, we avoid asking dynnaconf information as all
# delegated roles shares the same usage.
# 2. if we use self._online_key directly in the thread, it will raise
# an 'Settings' object has no attribute 'REDIS_SERVER'
online_key = copy.deepcopy(self._online_key)
expire_bins: int = self._settings.get_fresh("BINS_EXPIRATION")
signer = self._signer_store.get(online_key)

Check warning on line 708 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L706-L708

Added lines #L706 - L708 were not covered by tests

# function to process each delegated role
def process_delegated_role(delegated_name: str) -> str:
targets.signed.add_key(online_key, delegated_name)

Check warning on line 712 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L711-L712

Added lines #L711 - L712 were not covered by tests
bins_role = Metadata(Targets())
db_target_roles.append(
targets_schema.RSTUFTargetRoleCreate(
rolename=delegated_name, version=1
)
)
self._bump_expiry(bins_role, BINS)
self._sign(bins_role)
self._bump_expiry(bins_role, BINS, expire=expire_bins)
self._sign(bins_role, signer)

Check warning on line 720 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L719-L720

Added lines #L719 - L720 were not covered by tests
self._persist(bins_role, delegated_name)
delegated_roles.append(delegated_name)
kairoaraujo marked this conversation as resolved.
Show resolved Hide resolved
return delegated_name

Check warning on line 722 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L722

Added line #L722 was not covered by tests

with concurrent.futures.ThreadPoolExecutor() as executor:
start_time = time.time()
future_to_role = {

Check warning on line 726 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L724-L726

Added lines #L724 - L726 were not covered by tests
executor.submit(
process_delegated_role, delegated_name
): delegated_name
for delegated_name in succinct_roles.get_roles()
}
for future in concurrent.futures.as_completed(future_to_role):
MVrachev marked this conversation as resolved.
Show resolved Hide resolved
rolename = future.result()
snapshot.signed.meta[f"{rolename}.json"] = MetaFile(version=1)

Check warning on line 734 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L732-L734

Added lines #L732 - L734 were not covered by tests

targets_crud.create_roles(self._db, db_target_roles)
total_time = time.time() - start_time
logging.debug(

Check warning on line 738 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L737-L738

Added lines #L737 - L738 were not covered by tests
f"Added delegated roles hash bins in {total_time} seconds"
)

def _remove_delegated_role_keys(
self, targets: Metadata[Targets], delegated: DelegatedRole
Expand Down Expand Up @@ -1043,7 +1072,7 @@

else:
logging.info("Bootstrap using custom hash bin delegations")
self._add_metadata_hashbin_delegations(targets)
self._add_metadata_hashbin_delegations(targets, snapshot)

Check warning on line 1075 in repository_service_tuf_worker/repository.py

View check run for this annotation

Codecov / codecov/patch

repository_service_tuf_worker/repository.py#L1075

Added line #L1075 was not covered by tests

# Update expire, sign and persist the top level roles (`Targets`,
# `Timestamp``, `Snapshot`) in the backend storage service.
Expand Down
Loading