Skip to content

Commit 415c695

Browse files
authored
[ENG-9176] optimize verify; run verify before archiving only in dry mode (#11563)
1 parent f57b9a0 commit 415c695

3 files changed

Lines changed: 65 additions & 42 deletions

File tree

admin/nodes/views.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -845,11 +845,12 @@ def get(self, request, *args, **kwargs):
845845
messages.success(request, f"Registration {registration._id} is archived.")
846846
return redirect(self.get_success_url())
847847

848-
addons = set(registration.registered_from.get_addon_names())
849-
addons.update(DEFAULT_PERMISSIBLE_ADDONS)
848+
addons = set(DEFAULT_PERMISSIBLE_ADDONS)
849+
for reg in registration.node_and_primary_descendants():
850+
addons.update(reg.registered_from.get_addon_names())
850851

851852
try:
852-
archive_status = check(registration, permissible_addons=addons)
853+
archive_status = check(registration, permissible_addons=addons, verify_addons=False)
853854
messages.success(request, archive_status)
854855
except RegistrationStuckError as exc:
855856
messages.error(request, str(exc))
@@ -885,17 +886,21 @@ def post(self, request, *args, **kwargs):
885886
for reg in registration.node_and_primary_descendants():
886887
addons.update(reg.registered_from.get_addon_names())
887888

888-
try:
889-
verify(registration, permissible_addons=addons, raise_error=True)
890-
except ValidationError as exc:
891-
messages.error(request, str(exc))
892-
return redirect(self.get_success_url())
889+
# No need to verify addons during force archive,
890+
# because we fetched all permissible addons above
891+
verify_addons = False
893892

894-
dry_mode = force_archive_params.get('dry_mode', False)
895-
896-
if dry_mode:
897-
messages.success(request, f"Registration {registration._id} can be archived.")
893+
if force_archive_params.get('dry_mode', False):
894+
# For dry mode, verify synchronously to provide immediate feedback
895+
try:
896+
verify(registration, permissible_addons=addons, verify_addons=verify_addons, raise_error=True)
897+
messages.success(request, f"Registration {registration._id} can be archived.")
898+
except ValidationError as exc:
899+
messages.error(request, str(exc))
900+
return redirect(self.get_success_url())
898901
else:
902+
# For actual archiving, skip synchronous verification to avoid 502 timeouts
903+
# Verification will be performed asynchronously in the task
899904
force_archive_task = force_archive.delay(
900905
str(registration._id),
901906
permissible_addons=list(addons),

osf/management/commands/force_archive.py

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
from framework import sentry
3838
from framework.exceptions import HTTPError
3939
from osf import features
40-
from osf.models import Node, NodeLog, Registration, BaseFileNode
40+
from osf.models import Node, NodeLog, Registration, BaseFileNode, ArchiveJob
4141
from osf.models.files import TrashedFileNode
4242
from osf.utils.requests import get_current_request
4343
from osf.exceptions import RegistrationStuckRecoverableException, RegistrationStuckBrokenException
@@ -433,18 +433,29 @@ def archive_registrations(*args, **kwargs):
433433
ARCHIVED.append(reg)
434434
VERIFIED.remove(reg)
435435

436-
def verify(registration, permissible_addons=DEFAULT_PERMISSIBLE_ADDONS, raise_error=False):
436+
def verify(registration, permissible_addons=DEFAULT_PERMISSIBLE_ADDONS, verify_addons=True, raise_error=False):
437437
permissible_addons = set(permissible_addons)
438438
maybe_suppress_error = contextlib.suppress(ValidationError) if not raise_error else contextlib.nullcontext(enter_result=False)
439439

440-
for reg in registration.node_and_primary_descendants():
440+
# Collect all registrations and prefetch archive_jobs to avoid N+1 queries
441+
registrations = list(registration.node_and_primary_descendants())
442+
registration_ids = [reg.id for reg in registrations]
443+
444+
archive_jobs_dict = {
445+
job.dst_node_id: job
446+
for job in ArchiveJob.objects.filter(dst_node_id__in=registration_ids).only('dst_node_id', 'status')
447+
}
448+
449+
for reg in registrations:
441450
logger.info(f'Verifying {reg._id}')
442-
if reg.archive_job.status == ARCHIVER_SUCCESS:
451+
archive_job = archive_jobs_dict.get(reg.id) or getattr(reg, 'archive_job', None)
452+
if archive_job and archive_job.status == ARCHIVER_SUCCESS:
443453
continue
454+
444455
nonignorable_logs = get_logs_to_revert(reg)
445456
unacceptable_logs = nonignorable_logs.exclude(action__in=LOG_GREYLIST)
446457
if unacceptable_logs.exists():
447-
if len(permissible_addons) == 1 or unacceptable_logs.exclude(action__in=PERMISSIBLE_BLACKLIST):
458+
if len(permissible_addons) == 1 or unacceptable_logs.exclude(action__in=PERMISSIBLE_BLACKLIST).exists():
448459
message = '{}: Original node {} has unacceptable logs: {}'.format(
449460
registration._id,
450461
reg.registered_from._id,
@@ -456,30 +467,23 @@ def verify(registration, permissible_addons=DEFAULT_PERMISSIBLE_ADDONS, raise_er
456467
raise ValidationError(message)
457468

458469
return False
459-
if nonignorable_logs.filter(action__in=VERIFY_PROVIDER).exists():
460-
for log in nonignorable_logs.filter(action__in=VERIFY_PROVIDER):
461-
for key in ['source', 'destination']:
462-
if key in log.params:
463-
if log.params[key]['provider'] != 'osfstorage':
464-
message = '{}: {} Only OSFS moves and renames are permissible'.format(
465-
registration._id,
466-
log._id
467-
)
468-
logger.error(message)
469-
470-
with maybe_suppress_error:
471-
raise ValidationError(message)
472-
473-
return False
474-
addons = reg.registered_from.get_addon_names()
475-
if set(addons) - set(permissible_addons | {'wiki'}) != set():
476-
message = f'{registration._id}: Original node {reg.registered_from._id} has addons: {addons}'
477-
logger.error(message)
478-
479-
with maybe_suppress_error:
480-
raise ValidationError(message)
481-
482-
return False
470+
471+
verify_provider_logs = nonignorable_logs.filter(action__in=VERIFY_PROVIDER).only('params', '_id')
472+
for log in verify_provider_logs:
473+
for key in ['source', 'destination']:
474+
if key in log.params:
475+
if log.params[key].get('provider') != 'osfstorage':
476+
message = '{}: {} Only OSFS moves and renames are permissible'.format(
477+
registration._id,
478+
log._id
479+
)
480+
logger.error(message)
481+
482+
with maybe_suppress_error:
483+
raise ValidationError(message)
484+
485+
return False
486+
483487
if nonignorable_logs.exists():
484488
logger.info('{}: Original node {} has had revertable file operations'.format(
485489
registration._id,
@@ -490,6 +494,18 @@ def verify(registration, permissible_addons=DEFAULT_PERMISSIBLE_ADDONS, raise_er
490494
registration._id,
491495
reg.registered_from._id
492496
))
497+
498+
if verify_addons:
499+
addons = reg.registered_from.get_addon_names()
500+
if set(addons) - set(permissible_addons | {'wiki'}) != set():
501+
message = f'{registration._id}: Original node {reg.registered_from._id} has addons: {addons}'
502+
logger.error(message)
503+
504+
with maybe_suppress_error:
505+
raise ValidationError(message)
506+
507+
return False
508+
493509
return True
494510

495511
def verify_registrations(registration_ids, permissible_addons):

website/archiver/tasks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ def archive_success(self, dst_pk, job_pk):
375375

376376
@celery_app.task(bind=True)
377377
def force_archive(self, registration_id, permissible_addons, allow_unconfigured=False, skip_collisions=False, delete_collisions=False):
378-
from osf.management.commands.force_archive import archive
378+
from osf.management.commands.force_archive import archive, verify
379379

380380
create_app_context()
381381

@@ -384,6 +384,8 @@ def force_archive(self, registration_id, permissible_addons, allow_unconfigured=
384384
if not registration or not isinstance(registration, Registration):
385385
return f'Registration {registration_id} not found'
386386

387+
verify(registration, permissible_addons=set(permissible_addons), raise_error=True)
388+
387389
archive(
388390
registration,
389391
permissible_addons=set(permissible_addons),

0 commit comments

Comments
 (0)