From 3984f04d5835abe139737d37fea0227763f7fae9 Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Fri, 21 Feb 2025 15:07:44 +0100 Subject: [PATCH 1/3] Remove custom scroll handling. --- pkg/web_app/lib/script.dart | 2 - pkg/web_app/lib/src/scroll.dart | 78 --------------------------------- 2 files changed, 80 deletions(-) delete mode 100644 pkg/web_app/lib/src/scroll.dart diff --git a/pkg/web_app/lib/script.dart b/pkg/web_app/lib/script.dart index afd925eff2..7e4749c8a4 100644 --- a/pkg/web_app/lib/script.dart +++ b/pkg/web_app/lib/script.dart @@ -16,7 +16,6 @@ import 'src/likes.dart'; import 'src/mobile_nav.dart'; import 'src/page_updater.dart'; import 'src/screenshot_carousel.dart'; -import 'src/scroll.dart'; import 'src/search.dart'; import 'src/widget/widget.dart' show setupWidgets; @@ -36,7 +35,6 @@ void main() { void _setupAllEvents() { setupSearch(); - setupScroll(); setupFoldable(); setupHoverable(); setupMobileNav(); diff --git a/pkg/web_app/lib/src/scroll.dart b/pkg/web_app/lib/src/scroll.dart deleted file mode 100644 index 8544c3ba8b..0000000000 --- a/pkg/web_app/lib/src/scroll.dart +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// TODO: migrate to package:web -// ignore: deprecated_member_use -import 'dart:html'; - -void setupScroll() { - _setEventForAnchorScroll(); - window.onHashChange.listen((_) { - _scrollToHash(); - }); - _scrollToHash(); -} - -void _scrollToHash() { - final hash = window.location.hash; - if (hash.isNotEmpty) { - final id = hash.startsWith('#') ? hash.substring(1) : hash; - final list = - document.querySelectorAll('[id="${Uri.encodeQueryComponent(id)}"]'); - if (list.isEmpty) { - return; - } - // if there is an element on the current tab, scroll to it - for (final e in list) { - if (e.offsetHeight > 0) { - _scrollTo(e); - return; - } - } - // fallback, should not happen - _scrollTo(list.first); - } -} - -void _setEventForAnchorScroll() { - document.body!.onClick.listen((e) { - // locate the tag - var target = e.target as Element?; - while (target != null && - target.tagName.toLowerCase() != 'a' && - target.tagName.toLowerCase() != 'body') { - target = target.parent; - } - if (target is AnchorElement && - target.getAttribute('href') == target.hash && - (target.hash?.isNotEmpty ?? false)) { - final elem = document.querySelector(target.hash!); - if (elem != null) { - window.history - .pushState({}, document.title, target.hash); - e.preventDefault(); - _scrollTo(elem); - } - } - }); -} - -Future _scrollTo(Element elem) async { - // Chrome could provide inconsistent position data just after the page has - // been loaded. The first animation frame makes sure that the rendering is - // stabilized and the position data is correct. - await window.animationFrame; - final int stepCount = 30; - for (int i = 0; i < stepCount; i++) { - await window.animationFrame; - final int offsetTop = elem.offsetTop - 12; - final int scrollY = window.scrollY; - final int diff = offsetTop - scrollY; - // Stop early if the browser already jumped to it. - if (i == 0 && diff <= 12) { - break; - } - window.scrollTo(window.scrollX, scrollY + diff * (i + 1) ~/ stepCount); - } -} From 57bf459bc254b85f08808e37df3e8c90bdb9686d Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Mon, 3 Feb 2025 17:54:05 +0100 Subject: [PATCH 2/3] Strict index builder timeout + exceptions are not catched. --- app/lib/search/backend.dart | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/app/lib/search/backend.dart b/app/lib/search/backend.dart index f7a5775478..dd80812dcf 100644 --- a/app/lib/search/backend.dart +++ b/app/lib/search/backend.dart @@ -19,6 +19,7 @@ import 'package:meta/meta.dart'; // ignore: implementation_imports import 'package:pana/src/dartdoc/pub_dartdoc_data.dart'; import 'package:pool/pool.dart'; +import 'package:pub_dev/shared/monitoring.dart'; import 'package:retry/retry.dart'; import '../../publisher/backend.dart'; @@ -55,6 +56,10 @@ final Logger _logger = Logger('pub.search.backend'); /// building or updating the snapshot. const _defaultSnapshotBuildConcurrency = 8; +/// The (approximate) amount of time while the process holds the lock +/// and works on index building and updates. +const _maxLockHoldPeriod = Duration(days: 1); + /// Sets the backend service. void registerSearchBackend(SearchBackend backend) => ss.register(#_searchBackend, backend); @@ -101,15 +106,28 @@ class SearchBackend { '$runtimeVersion/search/update-snapshot', expiration: Duration(minutes: 20), ); + final started = clock.now(); while (true) { try { await lock.withClaim((claim) async { - await doCreateAndUpdateSnapshot(claim); + // Force timeout exception if the process does not release the lock. + final lockHoldTimeout = _maxLockHoldPeriod + Duration(hours: 1); + await doCreateAndUpdateSnapshot(claim).timeout(lockHoldTimeout); }); } catch (e, st) { - _logger.warning('Snapshot update failed.', e, st); + _logger.pubNoticeShout( + 'snapshot-building', 'Snapshot update failed.', e, st); + // Force waiting at least an hour before we rethrow the exception + final elapsed = clock.now().difference(started); + if (elapsed < Duration(hours: 1)) { + _logger.warning('Waiting before rethrowing exception.', e, st); + await Future.delayed(Duration(hours: 1) - elapsed); + } + // Throwing here will crash the VM and force the instance to restart. + rethrow; } - // Wait for 1 minutes for sanity, before trying again. + + // Allow another instance to get the lock and build the index. await Future.delayed(Duration(minutes: 1)); } } @@ -124,7 +142,7 @@ class SearchBackend { // The claim will be released after a day, another process may // start to build the snapshot from scratch again. - final workUntil = clock.now().add(Duration(days: 1)); + final workUntil = clock.now().add(_maxLockHoldPeriod); // creating snapshot from scratch final snapshot = SearchSnapshot(); From dc4c7be8b1cba41385299c04c6a30cd1bc8b3f1f Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Tue, 18 Feb 2025 15:56:37 +0100 Subject: [PATCH 3/3] More comment on the goals of the delay. --- app/lib/search/backend.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/search/backend.dart b/app/lib/search/backend.dart index dd80812dcf..c3fca24f6d 100644 --- a/app/lib/search/backend.dart +++ b/app/lib/search/backend.dart @@ -117,7 +117,9 @@ class SearchBackend { } catch (e, st) { _logger.pubNoticeShout( 'snapshot-building', 'Snapshot update failed.', e, st); - // Force waiting at least an hour before we rethrow the exception + // Force waiting at least an hour before we rethrow the exception, + // otherwise we could get into a reboot loop that doesn't get much + // real work done on the other tasks. final elapsed = clock.now().difference(started); if (elapsed < Duration(hours: 1)) { _logger.warning('Waiting before rethrowing exception.', e, st);