bugsink 0.1.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bugsink-0.1.12/.github/workflows/ci.yml +79 -0
- bugsink-0.1.12/.gitignore +51 -0
- bugsink-0.1.12/DESIGN-alerts.md +77 -0
- bugsink-0.1.12/DESIGN-breadcrumbs.md +5 -0
- bugsink-0.1.12/DESIGN-connections.md +76 -0
- bugsink-0.1.12/DESIGN-log-messages.md +6 -0
- bugsink-0.1.12/DESIGN-orgs-teams.md +18 -0
- bugsink-0.1.12/DESIGN-performance.md +76 -0
- bugsink-0.1.12/DESIGN-process-model.md +52 -0
- bugsink-0.1.12/DESIGN.md +86 -0
- bugsink-0.1.12/Dockerfile +37 -0
- bugsink-0.1.12/Dockerfile.db +7 -0
- bugsink-0.1.12/LICENSE +47 -0
- bugsink-0.1.12/PKG-INFO +84 -0
- bugsink-0.1.12/README.md +9 -0
- bugsink-0.1.12/alerts/__init__.py +0 -0
- bugsink-0.1.12/alerts/admin.py +1 -0
- bugsink-0.1.12/alerts/apps.py +6 -0
- bugsink-0.1.12/alerts/migrations/__init__.py +0 -0
- bugsink-0.1.12/alerts/models.py +1 -0
- bugsink-0.1.12/alerts/tasks.py +86 -0
- bugsink-0.1.12/alerts/templates/mails/issue_alert.html +527 -0
- bugsink-0.1.12/alerts/templates/mails/issue_alert.txt +12 -0
- bugsink-0.1.12/alerts/tests.py +134 -0
- bugsink-0.1.12/alerts/views.py +19 -0
- bugsink-0.1.12/api/LICENSE +10 -0
- bugsink-0.1.12/api/README.md +51 -0
- bugsink-0.1.12/api/event.schema.json +4321 -0
- bugsink-0.1.12/assets/04d16c3eff614748b759d4c52fa92aff5eb3b8c6.webp +0 -0
- bugsink-0.1.12/assets/0d0bedb635b44d99b9b1cca084a2ef80c6005e84.webp +0 -0
- bugsink-0.1.12/assets/1670e81db185490ab05bc83368b5f741.webp +0 -0
- bugsink-0.1.12/assets/1804883e2eba4aac82c45bf1f5ee6924c6005e82-2.webp +0 -0
- bugsink-0.1.12/assets/1804883e2eba4aac82c45bf1f5ee6924c6005e82.webp +0 -0
- bugsink-0.1.12/assets/187e9877407748bebd70be23771a1c0a9753c842.webp +0 -0
- bugsink-0.1.12/assets/1d37517924814e75942097098c74fe00c6005e82.webp +0 -0
- bugsink-0.1.12/assets/46cfd4b0046e48ca9beaefabcce233329753c844.webp +0 -0
- bugsink-0.1.12/assets/526c29422982427f8555d08a91e38d32c6005e83.webp +0 -0
- bugsink-0.1.12/assets/62e873c78f204679a9daaf911ac79e11778a75d4.webp +0 -0
- bugsink-0.1.12/assets/7183dec640db4b298bd518a683aaed6fc6005e83.webp +0 -0
- bugsink-0.1.12/assets/7c4b39bd49b549ebbdb7859e03029b33c6005e80.webp +0 -0
- bugsink-0.1.12/assets/93a1913577e2403da96af73a4ca1f2154501f2a0.webp +0 -0
- bugsink-0.1.12/assets/98ac676ef12643368442e08b5c4591ecc6005e83.webp +0 -0
- bugsink-0.1.12/assets/ab948b8962534410a94735bc8b90fdc19d2270f2.webp +0 -0
- bugsink-0.1.12/assets/b3f9a6541efc48cf982a5f3514e80bf9.webp +0 -0
- bugsink-0.1.12/assets/b5780a5c9d6d4851b146ff4967d53a02c6005e80.webp +0 -0
- bugsink-0.1.12/assets/bigbug-32x32.png +0 -0
- bugsink-0.1.12/assets/bigbug-64-64.png +0 -0
- bugsink-0.1.12/assets/blue-32x32.png +0 -0
- bugsink-0.1.12/assets/blue-64x64-manual-changes.png +0 -0
- bugsink-0.1.12/assets/blue-64x64-manual-changes.xcf +0 -0
- bugsink-0.1.12/assets/blue-64x64.webp +0 -0
- bugsink-0.1.12/assets/c4fb59360a9c4160809526e974414489.webp +0 -0
- bugsink-0.1.12/assets/c8aa0483abb343a6a67c89b3cd393336.webp +0 -0
- bugsink-0.1.12/assets/f390b92d28794a21869d473210e21d97c6005e84.webp +0 -0
- bugsink-0.1.12/assets/fe3212cbc7f042b9a77ad476011f669f9d2270f0.webp +0 -0
- bugsink-0.1.12/assets/newspaper.jpeg +0 -0
- bugsink-0.1.12/assets/sentry-sdk-issues/README.md +1 -0
- bugsink-0.1.12/assets/sentry-sdk-issues/django-templates.md +12 -0
- bugsink-0.1.12/assets/sites-and-prompts.txt +5 -0
- bugsink-0.1.12/assets/yello-64x64.png +0 -0
- bugsink-0.1.12/assets/yellow-32x32.png +0 -0
- bugsink-0.1.12/bugsink/__init__.py +26 -0
- bugsink-0.1.12/bugsink/_version.py +16 -0
- bugsink-0.1.12/bugsink/app_settings.py +106 -0
- bugsink-0.1.12/bugsink/conf_templates/docker.py.template +132 -0
- bugsink-0.1.12/bugsink/conf_templates/local.py.template +63 -0
- bugsink-0.1.12/bugsink/conf_templates/recommended.py.template +89 -0
- bugsink-0.1.12/bugsink/context_processors.py +15 -0
- bugsink-0.1.12/bugsink/dbrouters.py +17 -0
- bugsink-0.1.12/bugsink/debug_views.py +232 -0
- bugsink-0.1.12/bugsink/decorators.py +112 -0
- bugsink-0.1.12/bugsink/email_backends.py +10 -0
- bugsink-0.1.12/bugsink/event_schema.py +39730 -0
- bugsink-0.1.12/bugsink/exceptions.py +2 -0
- bugsink-0.1.12/bugsink/middleware.py +76 -0
- bugsink-0.1.12/bugsink/moreiterutils.py +36 -0
- bugsink-0.1.12/bugsink/period_utils.py +19 -0
- bugsink-0.1.12/bugsink/pygments_extensions.py +110 -0
- bugsink-0.1.12/bugsink/scripts/__init__.py +0 -0
- bugsink-0.1.12/bugsink/scripts/create_conf.py +67 -0
- bugsink-0.1.12/bugsink/scripts/manage.py +33 -0
- bugsink-0.1.12/bugsink/scripts/runsnappea.py +31 -0
- bugsink-0.1.12/bugsink/scripts/show_version.py +7 -0
- bugsink-0.1.12/bugsink/settings/__init__.py +0 -0
- bugsink-0.1.12/bugsink/settings/default.py +277 -0
- bugsink-0.1.12/bugsink/settings/development.py +158 -0
- bugsink-0.1.12/bugsink/streams.py +171 -0
- bugsink-0.1.12/bugsink/tests.py +363 -0
- bugsink-0.1.12/bugsink/timed_sqlite_backend/__init__.py +0 -0
- bugsink-0.1.12/bugsink/timed_sqlite_backend/base.py +86 -0
- bugsink-0.1.12/bugsink/tooling.py +12 -0
- bugsink-0.1.12/bugsink/transaction.py +184 -0
- bugsink-0.1.12/bugsink/urls.py +61 -0
- bugsink-0.1.12/bugsink/utils.py +33 -0
- bugsink-0.1.12/bugsink/version.py +8 -0
- bugsink-0.1.12/bugsink/views.py +83 -0
- bugsink-0.1.12/bugsink/volume_based_condition.py +23 -0
- bugsink-0.1.12/bugsink/wsgi.py +16 -0
- bugsink-0.1.12/bugsink.egg-info/PKG-INFO +84 -0
- bugsink-0.1.12/bugsink.egg-info/SOURCES.txt +374 -0
- bugsink-0.1.12/bugsink.egg-info/dependency_links.txt +1 -0
- bugsink-0.1.12/bugsink.egg-info/entry_points.txt +5 -0
- bugsink-0.1.12/bugsink.egg-info/requires.txt +17 -0
- bugsink-0.1.12/bugsink.egg-info/top_level.txt +18 -0
- bugsink-0.1.12/builddocker.bash +29 -0
- bugsink-0.1.12/buildxdocker.bash +29 -0
- bugsink-0.1.12/compat/__init__.py +0 -0
- bugsink-0.1.12/compat/auth.py +16 -0
- bugsink-0.1.12/compat/dsn.py +56 -0
- bugsink-0.1.12/compat/tests.py +102 -0
- bugsink-0.1.12/compat/timestamp.py +31 -0
- bugsink-0.1.12/compat/vars.py +27 -0
- bugsink-0.1.12/cornless-interrupt-sqlite.py +54 -0
- bugsink-0.1.12/cornless-print-other-thread.py +54 -0
- bugsink-0.1.12/cornless-set-progress-handler.py +66 -0
- bugsink-0.1.12/cornless.py +21 -0
- bugsink-0.1.12/events/__init__.py +0 -0
- bugsink-0.1.12/events/admin.py +98 -0
- bugsink-0.1.12/events/apps.py +20 -0
- bugsink-0.1.12/events/factories.py +50 -0
- bugsink-0.1.12/events/management/__init__.py +0 -0
- bugsink-0.1.12/events/management/commands/__init__.py +0 -0
- bugsink-0.1.12/events/management/commands/cleanup_events.py +19 -0
- bugsink-0.1.12/events/management/commands/make_consistent.py +41 -0
- bugsink-0.1.12/events/management/commands/store_events.py +24 -0
- bugsink-0.1.12/events/migrations/0001_initial.py +42 -0
- bugsink-0.1.12/events/migrations/0002_initial.py +20 -0
- bugsink-0.1.12/events/migrations/0003_initial.py +24 -0
- bugsink-0.1.12/events/migrations/0004_event_irrelevance_for_retention.py +19 -0
- bugsink-0.1.12/events/migrations/0005_event_events_even_project_abe572_idx.py +17 -0
- bugsink-0.1.12/events/migrations/0006_event_never_evict.py +18 -0
- bugsink-0.1.12/events/migrations/0007_set_never_evict.py +18 -0
- bugsink-0.1.12/events/migrations/0008_remove_event_events_even_project_abe572_idx_and_more.py +21 -0
- bugsink-0.1.12/events/migrations/0009_event_events_even_issue_i_90497b_idx.py +17 -0
- bugsink-0.1.12/events/migrations/0010_rename_ingest_order_event_digest_order_and_more.py +24 -0
- bugsink-0.1.12/events/migrations/0011_remove_event_events_even_project_adcdee_idx_and_more.py +34 -0
- bugsink-0.1.12/events/migrations/0012_event_ingested_at.py +20 -0
- bugsink-0.1.12/events/migrations/0013_harmonize_foogested_at.py +25 -0
- bugsink-0.1.12/events/migrations/__init__.py +0 -0
- bugsink-0.1.12/events/models.py +247 -0
- bugsink-0.1.12/events/retention.py +326 -0
- bugsink-0.1.12/events/retention_insight.py +64 -0
- bugsink-0.1.12/events/retention_simulator.py +183 -0
- bugsink-0.1.12/events/templates/events/event_stacktrace.txt +6 -0
- bugsink-0.1.12/events/tests.py +96 -0
- bugsink-0.1.12/events/ua_stuff.py +45 -0
- bugsink-0.1.12/events/urls.py +11 -0
- bugsink-0.1.12/events/views.py +27 -0
- bugsink-0.1.12/ingest/__init__.py +0 -0
- bugsink-0.1.12/ingest/admin.py +0 -0
- bugsink-0.1.12/ingest/apps.py +6 -0
- bugsink-0.1.12/ingest/event_counter.py +70 -0
- bugsink-0.1.12/ingest/filestore.py +10 -0
- bugsink-0.1.12/ingest/management/__init__.py +0 -0
- bugsink-0.1.12/ingest/management/commands/__init__.py +0 -0
- bugsink-0.1.12/ingest/management/commands/check_migrations.py +24 -0
- bugsink-0.1.12/ingest/management/commands/fetch_event_schema_json.py +52 -0
- bugsink-0.1.12/ingest/management/commands/raise_exception.py +49 -0
- bugsink-0.1.12/ingest/management/commands/send_json.py +157 -0
- bugsink-0.1.12/ingest/management/commands/showstat.py +19 -0
- bugsink-0.1.12/ingest/management/commands/stress_test.py +210 -0
- bugsink-0.1.12/ingest/migrations/0001_set_sqlite_wal.py +91 -0
- bugsink-0.1.12/ingest/migrations/__init__.py +0 -0
- bugsink-0.1.12/ingest/models.py +0 -0
- bugsink-0.1.12/ingest/parsers.py +190 -0
- bugsink-0.1.12/ingest/tasks.py +26 -0
- bugsink-0.1.12/ingest/tests.py +778 -0
- bugsink-0.1.12/ingest/urls.py +9 -0
- bugsink-0.1.12/ingest/views.py +548 -0
- bugsink-0.1.12/issues/__init__.py +0 -0
- bugsink-0.1.12/issues/admin.py +78 -0
- bugsink-0.1.12/issues/apps.py +6 -0
- bugsink-0.1.12/issues/factories.py +46 -0
- bugsink-0.1.12/issues/forms.py +29 -0
- bugsink-0.1.12/issues/migrations/0001_initial.py +60 -0
- bugsink-0.1.12/issues/migrations/0002_initial.py +53 -0
- bugsink-0.1.12/issues/migrations/0003_alter_turningpoint_triggering_event.py +20 -0
- bugsink-0.1.12/issues/migrations/0004_rename_event_count_issue_digested_event_count.py +18 -0
- bugsink-0.1.12/issues/migrations/0005_rename_ingest_order_issue_digest_order_and_more.py +23 -0
- bugsink-0.1.12/issues/migrations/0006_issue_next_unmute_check.py +18 -0
- bugsink-0.1.12/issues/migrations/__init__.py +0 -0
- bugsink-0.1.12/issues/models.py +422 -0
- bugsink-0.1.12/issues/regressions.py +85 -0
- bugsink-0.1.12/issues/templates/issues/_event_nav.html +40 -0
- bugsink-0.1.12/issues/templates/issues/base.html +241 -0
- bugsink-0.1.12/issues/templates/issues/breadcrumbs.html +63 -0
- bugsink-0.1.12/issues/templates/issues/event_404.html +46 -0
- bugsink-0.1.12/issues/templates/issues/event_details.html +206 -0
- bugsink-0.1.12/issues/templates/issues/event_list.html +170 -0
- bugsink-0.1.12/issues/templates/issues/grouping.html +13 -0
- bugsink-0.1.12/issues/templates/issues/history.html +173 -0
- bugsink-0.1.12/issues/templates/issues/issue_list.html +227 -0
- bugsink-0.1.12/issues/templates/issues/stacktrace.html +169 -0
- bugsink-0.1.12/issues/tests.py +645 -0
- bugsink-0.1.12/issues/urls.py +67 -0
- bugsink-0.1.12/issues/utils.py +148 -0
- bugsink-0.1.12/issues/views.py +557 -0
- bugsink-0.1.12/manage.py +22 -0
- bugsink-0.1.12/performance/__init__.py +0 -0
- bugsink-0.1.12/performance/bursty_data.py +66 -0
- bugsink-0.1.12/performance/context_managers.py +48 -0
- bugsink-0.1.12/performance/management/__init__.py +0 -0
- bugsink-0.1.12/performance/management/commands/__init__.py +0 -0
- bugsink-0.1.12/performance/stress-with-eviction/README.md +21 -0
- bugsink-0.1.12/performance/stress-with-eviction/df-day.png +0 -0
- bugsink-0.1.12/performance/stress-with-eviction/event_ingest_count-day.png +0 -0
- bugsink-0.1.12/performance/stress-with-eviction/response_time_api_avg-day.png +0 -0
- bugsink-0.1.12/performance/stress-with-eviction/response_time_api_max-day.png +0 -0
- bugsink-0.1.12/performance/stress-with-eviction/snappea_queue_size-day.png +0 -0
- bugsink-0.1.12/performance/stress-with-eviction/total_requests-day.png +0 -0
- bugsink-0.1.12/pre-commit +8 -0
- bugsink-0.1.12/projects/__init__.py +0 -0
- bugsink-0.1.12/projects/admin.py +70 -0
- bugsink-0.1.12/projects/apps.py +6 -0
- bugsink-0.1.12/projects/context_processors.py +9 -0
- bugsink-0.1.12/projects/forms.py +119 -0
- bugsink-0.1.12/projects/migrations/0001_initial.py +38 -0
- bugsink-0.1.12/projects/migrations/0002_initial.py +36 -0
- bugsink-0.1.12/projects/migrations/0003_project_retention_max_event_count.py +16 -0
- bugsink-0.1.12/projects/migrations/0004_project_quota_exceeded_until.py +18 -0
- bugsink-0.1.12/projects/migrations/0005_project_ingested_event_count.py +18 -0
- bugsink-0.1.12/projects/migrations/0006_initial_ingested_count_value.py +27 -0
- bugsink-0.1.12/projects/migrations/0007_rename_ingested_event_count_project_digested_event_count.py +20 -0
- bugsink-0.1.12/projects/migrations/0008_project_next_quota_check.py +18 -0
- bugsink-0.1.12/projects/migrations/__init__.py +0 -0
- bugsink-0.1.12/projects/models.py +132 -0
- bugsink-0.1.12/projects/tasks.py +47 -0
- bugsink-0.1.12/projects/templates/mails/project_membership_invite.html +519 -0
- bugsink-0.1.12/projects/templates/mails/project_membership_invite.txt +5 -0
- bugsink-0.1.12/projects/templates/mails/project_membership_invite_new_user.html +519 -0
- bugsink-0.1.12/projects/templates/mails/project_membership_invite_new_user.txt +5 -0
- bugsink-0.1.12/projects/templates/projects/project_edit.html +36 -0
- bugsink-0.1.12/projects/templates/projects/project_list.html +162 -0
- bugsink-0.1.12/projects/templates/projects/project_member_settings.html +53 -0
- bugsink-0.1.12/projects/templates/projects/project_members.html +94 -0
- bugsink-0.1.12/projects/templates/projects/project_members_accept.html +31 -0
- bugsink-0.1.12/projects/templates/projects/project_members_invite.html +58 -0
- bugsink-0.1.12/projects/templates/projects/project_new.html +32 -0
- bugsink-0.1.12/projects/templates/projects/project_sdk_setup.html +137 -0
- bugsink-0.1.12/projects/tests.py +1 -0
- bugsink-0.1.12/projects/urls.py +23 -0
- bugsink-0.1.12/projects/views.py +392 -0
- bugsink-0.1.12/pyproject.toml +61 -0
- bugsink-0.1.12/releases/__init__.py +0 -0
- bugsink-0.1.12/releases/admin.py +25 -0
- bugsink-0.1.12/releases/apps.py +6 -0
- bugsink-0.1.12/releases/migrations/0001_initial.py +31 -0
- bugsink-0.1.12/releases/migrations/__init__.py +0 -0
- bugsink-0.1.12/releases/models.py +161 -0
- bugsink-0.1.12/releases/tests.py +59 -0
- bugsink-0.1.12/releases/views.py +1 -0
- bugsink-0.1.12/requirements.development.txt +2 -0
- bugsink-0.1.12/requirements.txt +17 -0
- bugsink-0.1.12/sentry/LICENSE +12 -0
- bugsink-0.1.12/sentry/__init__.py +0 -0
- bugsink-0.1.12/sentry/stacktraces/functions.py +228 -0
- bugsink-0.1.12/sentry/stacktraces/platform.py +10 -0
- bugsink-0.1.12/sentry/stacktraces/processing.py +41 -0
- bugsink-0.1.12/sentry/utils/safe.py +95 -0
- bugsink-0.1.12/sentry/utils/strings.py +7 -0
- bugsink-0.1.12/sentry_sdk_extensions/__init__.py +113 -0
- bugsink-0.1.12/sentry_sdk_extensions/nohub.py +133 -0
- bugsink-0.1.12/sentry_sdk_extensions/transport.py +13 -0
- bugsink-0.1.12/setup.cfg +4 -0
- bugsink-0.1.12/snappea/__init__.py +50 -0
- bugsink-0.1.12/snappea/admin.py +1 -0
- bugsink-0.1.12/snappea/apps.py +6 -0
- bugsink-0.1.12/snappea/datastructures.py +30 -0
- bugsink-0.1.12/snappea/decorators.py +39 -0
- bugsink-0.1.12/snappea/example_tasks.py +25 -0
- bugsink-0.1.12/snappea/foreman.py +376 -0
- bugsink-0.1.12/snappea/management/__init__.py +0 -0
- bugsink-0.1.12/snappea/management/commands/__init__.py +0 -0
- bugsink-0.1.12/snappea/management/commands/checksnappea.py +11 -0
- bugsink-0.1.12/snappea/management/commands/runsnappea.py +10 -0
- bugsink-0.1.12/snappea/migrations/0001_initial.py +50 -0
- bugsink-0.1.12/snappea/migrations/0002_create_models.py +22 -0
- bugsink-0.1.12/snappea/migrations/__init__.py +0 -0
- bugsink-0.1.12/snappea/models.py +26 -0
- bugsink-0.1.12/snappea/settings.py +53 -0
- bugsink-0.1.12/snappea/tests.py +1 -0
- bugsink-0.1.12/snappea/views.py +1 -0
- bugsink-0.1.12/static/favicon.png +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-700.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-700italic.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-italic.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-mono-v19-cyrillic_cyrillic-ext_latin_latin-ext_vietnamese-regular.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-700.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-700italic.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-italic.woff2 +0 -0
- bugsink-0.1.12/static/fonts/ibm-plex-sans-v19-cyrillic_cyrillic-ext_greek_latin_latin-ext_vietnamese-regular.woff2 +0 -0
- bugsink-0.1.12/static/images/bugsink-logo.png +0 -0
- bugsink-0.1.12/static/js/issue_history.js +29 -0
- bugsink-0.1.12/static/js/issue_list.js +45 -0
- bugsink-0.1.12/static/js/issue_stacktrace.js +145 -0
- bugsink-0.1.12/static/js/project_list.js +6 -0
- bugsink-0.1.12/static/js/team_list.js +6 -0
- bugsink-0.1.12/teams/__init__.py +0 -0
- bugsink-0.1.12/teams/admin.py +61 -0
- bugsink-0.1.12/teams/apps.py +6 -0
- bugsink-0.1.12/teams/forms.py +62 -0
- bugsink-0.1.12/teams/migrations/0001_initial.py +33 -0
- bugsink-0.1.12/teams/migrations/0002_initial.py +25 -0
- bugsink-0.1.12/teams/migrations/__init__.py +0 -0
- bugsink-0.1.12/teams/models.py +50 -0
- bugsink-0.1.12/teams/tasks.py +47 -0
- bugsink-0.1.12/teams/templates/mails/team_membership_invite.html +519 -0
- bugsink-0.1.12/teams/templates/mails/team_membership_invite.txt +5 -0
- bugsink-0.1.12/teams/templates/mails/team_membership_invite_new_user.html +519 -0
- bugsink-0.1.12/teams/templates/mails/team_membership_invite_new_user.txt +5 -0
- bugsink-0.1.12/teams/templates/teams/team_edit.html +34 -0
- bugsink-0.1.12/teams/templates/teams/team_list.html +144 -0
- bugsink-0.1.12/teams/templates/teams/team_member_settings.html +53 -0
- bugsink-0.1.12/teams/templates/teams/team_members.html +93 -0
- bugsink-0.1.12/teams/templates/teams/team_members_accept.html +31 -0
- bugsink-0.1.12/teams/templates/teams/team_members_invite.html +60 -0
- bugsink-0.1.12/teams/templates/teams/team_new.html +31 -0
- bugsink-0.1.12/teams/tests.py +1 -0
- bugsink-0.1.12/teams/urls.py +19 -0
- bugsink-0.1.12/teams/views.py +364 -0
- bugsink-0.1.12/templates/403.html +11 -0
- bugsink-0.1.12/templates/403_csrf.html +19 -0
- bugsink-0.1.12/templates/404.html +11 -0
- bugsink-0.1.12/templates/500.html +12 -0
- bugsink-0.1.12/templates/admin/change_form_object_tools.html +10 -0
- bugsink-0.1.12/templates/bugsink/csrf_debug.html +88 -0
- bugsink-0.1.12/templates/bugsink/login.html +55 -0
- bugsink-0.1.12/templates/bugsink/settings.html +51 -0
- bugsink-0.1.12/templates/signup.html +37 -0
- bugsink-0.1.12/theme/__init__.py +0 -0
- bugsink-0.1.12/theme/apps.py +5 -0
- bugsink-0.1.12/theme/static/css/dist/styles.css +1 -0
- bugsink-0.1.12/theme/static_src/.gitignore +1 -0
- bugsink-0.1.12/theme/static_src/package-lock.json +1543 -0
- bugsink-0.1.12/theme/static_src/package.json +29 -0
- bugsink-0.1.12/theme/static_src/postcss.config.js +7 -0
- bugsink-0.1.12/theme/static_src/src/styles.css +212 -0
- bugsink-0.1.12/theme/static_src/tailwind.config.js +76 -0
- bugsink-0.1.12/theme/templates/bare_base.html +24 -0
- bugsink-0.1.12/theme/templates/barest_base.html +20 -0
- bugsink-0.1.12/theme/templates/base.html +52 -0
- bugsink-0.1.12/theme/templates/tailwind_forms/formfield.html +18 -0
- bugsink-0.1.12/theme/templatetags/__init__.py +0 -0
- bugsink-0.1.12/theme/templatetags/datetime_formatting.py +37 -0
- bugsink-0.1.12/theme/templatetags/issues.py +132 -0
- bugsink-0.1.12/theme/templatetags/stricter_templates.py +35 -0
- bugsink-0.1.12/theme/templatetags/tailwind_forms.py +32 -0
- bugsink-0.1.12/theme/templatetags/user.py +14 -0
- bugsink-0.1.12/theme/templatetags/version.py +9 -0
- bugsink-0.1.12/theme/tests.py +61 -0
- bugsink-0.1.12/tox.ini +6 -0
- bugsink-0.1.12/users/__init__.py +16 -0
- bugsink-0.1.12/users/admin.py +8 -0
- bugsink-0.1.12/users/apps.py +6 -0
- bugsink-0.1.12/users/forms.py +146 -0
- bugsink-0.1.12/users/migrations/0001_initial.py +54 -0
- bugsink-0.1.12/users/migrations/__init__.py +0 -0
- bugsink-0.1.12/users/models.py +31 -0
- bugsink-0.1.12/users/tasks.py +34 -0
- bugsink-0.1.12/users/templates/mails/confirm_email.html +519 -0
- bugsink-0.1.12/users/templates/mails/confirm_email.txt +5 -0
- bugsink-0.1.12/users/templates/mails/reset_password_email.html +519 -0
- bugsink-0.1.12/users/templates/mails/reset_password_email.txt +5 -0
- bugsink-0.1.12/users/templates/users/confirm_email.html +29 -0
- bugsink-0.1.12/users/templates/users/confirm_email_sent.html +23 -0
- bugsink-0.1.12/users/templates/users/logged_out.html +21 -0
- bugsink-0.1.12/users/templates/users/preferences.html +37 -0
- bugsink-0.1.12/users/templates/users/request_reset_password.html +33 -0
- bugsink-0.1.12/users/templates/users/resend_confirmation.html +34 -0
- bugsink-0.1.12/users/templates/users/reset_password.html +37 -0
- bugsink-0.1.12/users/templates/users/reset_password_email_sent.html +21 -0
- bugsink-0.1.12/users/templates/users/user_edit.html +33 -0
- bugsink-0.1.12/users/templates/users/user_list.html +86 -0
- bugsink-0.1.12/users/tests.py +1 -0
- bugsink-0.1.12/users/urls.py +8 -0
- bugsink-0.1.12/users/views.py +246 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: Continuous Integration
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "main" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "main" ]
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
DJANGO_SETTINGS_MODULE: "bugsink.settings.development"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
flake8:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- name: "Set up Python 3.11"
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.11" # below 3.12 to avoid false positives inside f-string
|
|
21
|
+
- name: Install Flake8
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade flake8
|
|
24
|
+
- name: Run Flake8
|
|
25
|
+
run: |
|
|
26
|
+
# We ignore 2 classes of whitespace errors (which are useful in the local context,
|
|
27
|
+
# but not worth breaking the build).
|
|
28
|
+
# https://github.com/PyCQA/flake8/issues/515 shows a dead end of doing this "properly"
|
|
29
|
+
# so we just specify it on the command line
|
|
30
|
+
flake8 --extend-ignore=E127,E741,E501 `git ls-files | grep py$`
|
|
31
|
+
|
|
32
|
+
test:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
strategy:
|
|
35
|
+
matrix:
|
|
36
|
+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
40
|
+
uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: ${{ matrix.python-version }}
|
|
43
|
+
- name: Install build
|
|
44
|
+
run: |
|
|
45
|
+
python -m pip install --upgrade pip
|
|
46
|
+
pip install build
|
|
47
|
+
- name: Build wheel
|
|
48
|
+
run: |
|
|
49
|
+
python -m build --wheel
|
|
50
|
+
- name: Install from wheel
|
|
51
|
+
run: |
|
|
52
|
+
python -m pip install dist/*.whl
|
|
53
|
+
- name: Install development dependencies
|
|
54
|
+
run: |
|
|
55
|
+
pip install -r requirements.development.txt
|
|
56
|
+
- name: Check out event-samples
|
|
57
|
+
uses: actions/checkout@master
|
|
58
|
+
with:
|
|
59
|
+
repository: bugsink/event-samples
|
|
60
|
+
path: "event-samples"
|
|
61
|
+
- name: Create separate dir to avoid accidentally using non-packaged code
|
|
62
|
+
run: |
|
|
63
|
+
mkdir separate_dir
|
|
64
|
+
- name: Run Makemigrations --check
|
|
65
|
+
working-directory: separate_dir
|
|
66
|
+
run: |
|
|
67
|
+
bugsink-manage makemigrations --check
|
|
68
|
+
- name: Run Tests
|
|
69
|
+
working-directory: separate_dir
|
|
70
|
+
env:
|
|
71
|
+
SAMPLES_DIR: "${{ github.workspace }}/event-samples"
|
|
72
|
+
PYTHONWARNINGS: all
|
|
73
|
+
run: |
|
|
74
|
+
# because we're outside the project directory, the test discovery won't find our packages. We simply enumerate
|
|
75
|
+
# them using some shell-magic. Note that the only non-app that we still care about is 'bugsink' (project, not app)
|
|
76
|
+
# which we mention separately
|
|
77
|
+
bugsink-manage test `bugsink-manage shell -c 'from django.conf import settings; print(" ".join(settings.BUGSINK_APPS))'` bugsink -v2
|
|
78
|
+
# bugsink-manage test ${GITHUB_WORKSPACE} -v2 # fails with the following, which I don't understand:
|
|
79
|
+
# ImportError: 'tests' module incorrectly imported from '/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/alerts'. Expected '/home/runner/work/bugsink-private/bugsink-private/alerts'. Is this module globally installed?
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
*.pyc
|
|
3
|
+
__pycache__
|
|
4
|
+
|
|
5
|
+
# Virtual Environments / build
|
|
6
|
+
/.env
|
|
7
|
+
/.venv
|
|
8
|
+
/bin/
|
|
9
|
+
/include/
|
|
10
|
+
/lib/
|
|
11
|
+
/lib64
|
|
12
|
+
/pyvenv.cfg
|
|
13
|
+
/share/
|
|
14
|
+
/bugsink.egg-info/
|
|
15
|
+
/bugsink/_version.py
|
|
16
|
+
/dist/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# sqlite
|
|
20
|
+
/db.sqlite3
|
|
21
|
+
/db.*.sqlite3
|
|
22
|
+
/test.sqlite3
|
|
23
|
+
|
|
24
|
+
/db.sqlite3-wal
|
|
25
|
+
/db.*.sqlite3-wal
|
|
26
|
+
/test.sqlite3-wal
|
|
27
|
+
|
|
28
|
+
/db.sqlite3-shm
|
|
29
|
+
/db.*.sqlite3-shm
|
|
30
|
+
/test.sqlite3-shm
|
|
31
|
+
|
|
32
|
+
/snappea.sqlite3
|
|
33
|
+
/snappea.sqlite3-shm
|
|
34
|
+
/snappea.sqlite3-wal
|
|
35
|
+
|
|
36
|
+
# vim
|
|
37
|
+
*.swo
|
|
38
|
+
*.swp
|
|
39
|
+
|
|
40
|
+
# node (tailwind)
|
|
41
|
+
node_modules
|
|
42
|
+
/package*
|
|
43
|
+
|
|
44
|
+
# conf
|
|
45
|
+
/bugsink_conf.py
|
|
46
|
+
|
|
47
|
+
# static files
|
|
48
|
+
/collectedstatic
|
|
49
|
+
|
|
50
|
+
# Docker artifacts
|
|
51
|
+
/bugsink*tar
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Alert Rules
|
|
2
|
+
|
|
3
|
+
## Opinionated take
|
|
4
|
+
|
|
5
|
+
Default setting, may be adapted at organisational level.
|
|
6
|
+
|
|
7
|
+
You should get alerts for any state change for the worse, such as:
|
|
8
|
+
|
|
9
|
+
* New issues
|
|
10
|
+
* Regressions
|
|
11
|
+
* Unmuting (could be due to volume)
|
|
12
|
+
|
|
13
|
+
The basic belief underlying this is: "you should care about the errors on your project". And therefore, you'll be
|
|
14
|
+
alerted when they occur.
|
|
15
|
+
|
|
16
|
+
### Volume-Based Rules for unmuting
|
|
17
|
+
|
|
18
|
+
Volume based rules may be configured per-issue for unmuting. e.g.
|
|
19
|
+
|
|
20
|
+
- First time more than 5 events per the last 3 hours occur
|
|
21
|
+
- First time more than 10 events per day
|
|
22
|
+
- First time the total number of events exceeds 100
|
|
23
|
+
|
|
24
|
+
The idea is: "I know about this issue, I've determined it's not important, and I only want to get notified when it start
|
|
25
|
+
occurrung a lot (for some definition of 'a lot')"
|
|
26
|
+
|
|
27
|
+
The tie-in with alerts is: you can send notification on-unmute. (by the definition of unmuting, the alert
|
|
28
|
+
occurs only the _first time_ the condition occurs)
|
|
29
|
+
|
|
30
|
+
## "Later": auto-mute w/ default unmute rules
|
|
31
|
+
|
|
32
|
+
At some point we may make it so that you can configure per-project that issues are initially muted (on create) but with
|
|
33
|
+
some predefined set of volume-based unmute conditions, which is set on the muted issues on-create.
|
|
34
|
+
|
|
35
|
+
(This is in place of project-level volume-based alert settings. The main reason for this is that I want to keep a
|
|
36
|
+
symmetry/tracability between what's going on in the UI and the alerts that are going out)
|
|
37
|
+
|
|
38
|
+
## "Later": more than first-time-only
|
|
39
|
+
|
|
40
|
+
(The below only works if you let go of the idea that the only way you get volume-based alerts is when some unmute-condition occurs).
|
|
41
|
+
|
|
42
|
+
Rather than just having first-time only rules, you could have alerting rules that are triggered when a condition
|
|
43
|
+
persists. e.g.
|
|
44
|
+
|
|
45
|
+
* Any time more than 5 events occur the past 3 hours (ignoring any events that occured before the last time this was
|
|
46
|
+
triggered).
|
|
47
|
+
|
|
48
|
+
## Personal Notification Settings
|
|
49
|
+
|
|
50
|
+
Personal notification settings exist both globally (i.e. configured per User) and per Project.
|
|
51
|
+
|
|
52
|
+
Reasons for per-project settings include scenarios where someone is involved in the project but not to the extent of
|
|
53
|
+
needing constant updates (e.g., consulting members or certain leads).
|
|
54
|
+
|
|
55
|
+
However, these settings are limited to a single toggle: yes/no/(default). That is, you can't choose specific rules to
|
|
56
|
+
follow for a project (e.g. just on unmute, or just on regressions)
|
|
57
|
+
|
|
58
|
+
More detailed configuration of alerting rules is project-centered. The idea is that the 2 main variables that control
|
|
59
|
+
when alerts should be sent are dictated by the nature of the project, not the preferences of the member, namely:
|
|
60
|
+
|
|
61
|
+
* how broken the project is (how many errors are generated)
|
|
62
|
+
* how important brokenness is
|
|
63
|
+
|
|
64
|
+
I.e. there is a single (configurable) threshold per project for what constitues "worth alerting about but avoiding
|
|
65
|
+
swamping in false positives" which you can then subscribe to or not.
|
|
66
|
+
|
|
67
|
+
(Note that the thoughts in this section are already less necessary to make explicit, because most of the volume-based
|
|
68
|
+
alerting has (for now) been chosen not to implement in favor of vbc-unmute-rules (which are by definition not per user)
|
|
69
|
+
|
|
70
|
+
## Chat-Ops
|
|
71
|
+
|
|
72
|
+
You may configure any number of chat-ops endpoints (mattermost/slack channels). These are not connected to individual
|
|
73
|
+
users. There's likely a default setting at the organization level. But it's also well imaginable that some project-based
|
|
74
|
+
settings are necessary, e.g. when each project has its own channel.
|
|
75
|
+
|
|
76
|
+
The chat-ops channels always receive the per-project configured set of notifications, i.e. the threshold cannot be
|
|
77
|
+
changed per chat-op-channel (as with users).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Thoughts on breadcrumbs
|
|
2
|
+
|
|
3
|
+
I think it is only a moderately useful functionality; it seems like an idea they once had, but which was never _really_ followed through on. Perhaps you as a user can make it more useful by making more aggressive choices on the client side. But how to (e.g.) disable it in the Django Integration is not easy to find, and disabling useless things is always important if you really want to take something in hand. What happens with the logging integration is something I still have to read up. i.e. do certain logs just always become breadcrumbs? and how does that relate to logs that are automatically translated into events? (also read what I wrote about this in sentry-stfu)
|
|
4
|
+
|
|
5
|
+
And all those types and categories are only half thought through and defined, at least as far as the documentation is concerned. If I want to show such images it is a bit of reverse engineering (in combination with RTFM). It could be that in the JavaScript world, and combined with transactions, there is something more useful. I should see that then. As with log messages: we need some support for it, because the clients support this.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Connection closing and subsequent re-opening is not free. I've done some experiments; it looks like the connection
|
|
2
|
+
close/open themselves are in the sub-1ms range.
|
|
3
|
+
|
|
4
|
+
But: when being the first connection to write, there is a c. 13ms penalty. Hypothesis: the opening of the WAL file.
|
|
5
|
+
|
|
6
|
+
This is the code I played with to establish this.
|
|
7
|
+
Note that the result of running the second half (the Django part) without first running the first half are very
|
|
8
|
+
different from doing them both at once. Hence "being the first connection", i.e. there's a cross-connection effect.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
from performance.context_managers import time_it
|
|
12
|
+
import sqlite3
|
|
13
|
+
|
|
14
|
+
conn = sqlite3.connect('db.sqlite3')
|
|
15
|
+
cursor = conn.cursor()
|
|
16
|
+
|
|
17
|
+
with time_it() as timings:
|
|
18
|
+
cursor.execute('UPDATE auth_user set username = username')
|
|
19
|
+
conn.commit()
|
|
20
|
+
|
|
21
|
+
print("fresh conn", timings.took)
|
|
22
|
+
|
|
23
|
+
with time_it() as timings:
|
|
24
|
+
cursor.execute('UPDATE auth_user set username = username')
|
|
25
|
+
conn.commit()
|
|
26
|
+
|
|
27
|
+
print("reused conn", timings.took)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
from performance.context_managers import time_it
|
|
31
|
+
import sqlite3
|
|
32
|
+
from django.db import connection
|
|
33
|
+
from django.db import models
|
|
34
|
+
from users.models import User
|
|
35
|
+
from performance.context_managers import time_it
|
|
36
|
+
from bugsink.transaction import immediate_atomic
|
|
37
|
+
|
|
38
|
+
with time_it() as timings:
|
|
39
|
+
with immediate_atomic():
|
|
40
|
+
User.objects.update(username=models.F('username'))
|
|
41
|
+
|
|
42
|
+
print("django fresh conn", timings.took)
|
|
43
|
+
|
|
44
|
+
with time_it() as timings:
|
|
45
|
+
with immediate_atomic():
|
|
46
|
+
User.objects.update(username=models.F('username'))
|
|
47
|
+
|
|
48
|
+
print("django reused conn", timings.took)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With just the second half:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
... with immediate_atomic():
|
|
55
|
+
... User.objects.update(username=models.F('username'))
|
|
56
|
+
...
|
|
57
|
+
CONNECTION created /mnt/datacrypt/dev/bugsink/db.sqlite3 124812146634816 in 0ms MainThread
|
|
58
|
+
1.79ms BEGIN IMMEDIATE, A.K.A. get-write-lock ↴
|
|
59
|
+
0.69ms ABOUT TO END IMMEDIATE transaction' ↴
|
|
60
|
+
10.67ms END IMMEDIATE transaction' ↴
|
|
61
|
+
>>> print("django fresh conn", timings.took)
|
|
62
|
+
|
|
63
|
+
django fresh conn 13.312101364135742
|
|
64
|
+
|
|
65
|
+
>>>
|
|
66
|
+
>>> with time_it() as timings:
|
|
67
|
+
... with immediate_atomic():
|
|
68
|
+
... User.objects.update(username=models.F('username'))
|
|
69
|
+
...
|
|
70
|
+
0.19ms BEGIN IMMEDIATE, A.K.A. get-write-lock ↴
|
|
71
|
+
0.35ms ABOUT TO END IMMEDIATE transaction' ↴
|
|
72
|
+
0.50ms END IMMEDIATE transaction' ↴
|
|
73
|
+
>>> print("django reused conn", timings.took)
|
|
74
|
+
|
|
75
|
+
django reused conn 0.9033679962158203
|
|
76
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
We support Logging as good as we can, because we've taken the approach of being compatible with Sentry clients, and those occassionally send Log messages. We don't want to be confusing in that case. Having said that:
|
|
2
|
+
|
|
3
|
+
Bugsink is about Error handling, not about arbitrary logging. Looking at the competition (Sentry, GlicthTip), I don't think log messages show up particularly strong there: because the message is lifted to the main title element, and because there is barely any info to show (certainly no stacktrace), they are in fact more confusing than anything (we've at least taken a more explicit approach, of putting some general "Log Message" at the top, and showing the message where the exception messages are generally shown). Perhaps that logging shines when you see it through the lens of transactions (then again: perhaps not -- maybe they're just chasing the trend of "log everythign").
|
|
4
|
+
|
|
5
|
+
One further question: I see a lot of "error" levels in the messages as sent with some quick tests... even when those messages had lower log-levels.
|
|
6
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Thoughts on Teams and Organizations
|
|
2
|
+
|
|
3
|
+
Our approach doesn't incorporate a sentry-like model for "teams." Instead, individuals are either part of a project or
|
|
4
|
+
not. Envisioned roles are:
|
|
5
|
+
|
|
6
|
+
* read-only access
|
|
7
|
+
* regular (allowing state changes to issues)
|
|
8
|
+
* admin (with privileges to manage settings and memberships).
|
|
9
|
+
|
|
10
|
+
Regarding organizations, the use cases are:
|
|
11
|
+
|
|
12
|
+
* if you're employed at "big-co", it serves as a platform to establish your "bigger team," which could oversee multiple
|
|
13
|
+
see e.g. GitHub, GitLab, and CloudFoundry for comparison
|
|
14
|
+
* when I develop hosted.bugsink.com, the "organization" is the natural unit a user will have access to or not.
|
|
15
|
+
|
|
16
|
+
These organizations can serve as points for inherited permissions, as well as provide a systematic grouping of
|
|
17
|
+
projects. Additionally, they serve as a central repository for inheritable settings, streamlining the management and
|
|
18
|
+
configuration of multiple projects within a broader organizational context.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
## Some thoughts on performance
|
|
2
|
+
|
|
3
|
+
### 18 July surprise
|
|
4
|
+
|
|
5
|
+
In the last few days I've been observing 85/s requests ingestion. What has changed? No idea...
|
|
6
|
+
|
|
7
|
+
### Thoughts after -wal cleanup (better connection closing)
|
|
8
|
+
|
|
9
|
+
There seems to be a small drop in ingestion performance, to approx. 30/s events. Given that this was (in "Original
|
|
10
|
+
thoughts") the max, this is fine (for the increased (presumed) stability). (Pre-wal-cleanup this was approx 40/s)
|
|
11
|
+
|
|
12
|
+
If this becomes a problem, the thing to consider is "keeping threads, and associated connections, open in the foreman".
|
|
13
|
+
|
|
14
|
+
### Thoughts after implementing eviction
|
|
15
|
+
|
|
16
|
+
(See also contents of ./performance/stress-with-eviction/)
|
|
17
|
+
|
|
18
|
+
* Surprise 1: 50 events/s (60% more than what I found below)
|
|
19
|
+
* Surprise 2: in this config, I _am_ getting snappea backlog.
|
|
20
|
+
|
|
21
|
+
### Original thoughts
|
|
22
|
+
|
|
23
|
+
Now that we have playground.bugsink.com, I could get some real data on that system too.
|
|
24
|
+
|
|
25
|
+
I suppose the most "interesting" finding is that the ~30/s events I can handle seem to be entirely limited by the
|
|
26
|
+
(https?) nginx stack.
|
|
27
|
+
|
|
28
|
+
This also means that, in this setup, snappea is able to deal with "postponed" work basically as fast as the frontend can
|
|
29
|
+
deliver it, i.e. there is no actual backlog. Which raises some serious(?) questions about snappea in this setup.
|
|
30
|
+
|
|
31
|
+
Some things I played with (more or less in order I did them):
|
|
32
|
+
|
|
33
|
+
* try to remove the (physical) network from the equation by doing local-loopback
|
|
34
|
+
* use compression (brotli) to avoid network overhead
|
|
35
|
+
* compare with my local laptop
|
|
36
|
+
* drop actual handling of the request, i.e. just do a `request.read(); return HttpResponse()`
|
|
37
|
+
* remove nginx from the equation and just connect on `:8000`
|
|
38
|
+
|
|
39
|
+
Some numbers:
|
|
40
|
+
|
|
41
|
+
All measurements are with a 50k event. (NOTE: after removal of whitespace this is actually a 40k event)
|
|
42
|
+
|
|
43
|
+
* Starting point is ~30/s. local to playground; actual (non-immediate) handling of events. varying number of gunicorn
|
|
44
|
+
and snappea workers doesn't seem to do much.
|
|
45
|
+
|
|
46
|
+
* local loopback on playground.bugsink.com: ~21/s. i.e. it's slower. Presumably: the cost of running the stress test.
|
|
47
|
+
|
|
48
|
+
* local loopback on playground.bugsink.com, but dropping the request on the floor: ~25/s.
|
|
49
|
+
|
|
50
|
+
* compressing as brotli and doing local -> playgrond: ~18/s. Surprisingly the cost of unpacking is larger than the
|
|
51
|
+
advantage of having to deal with less data.
|
|
52
|
+
|
|
53
|
+
* locally (laptop), I got to ~280/s with actual handling turned on. This is where I (slightly) outrun snappea.
|
|
54
|
+
|
|
55
|
+
* locally with drop-to-floor I got to ~455/s. Noteworthy: this is not even twice as fast as the "real" (postponed)
|
|
56
|
+
handling. i.e. we're already close to our limits with that.
|
|
57
|
+
|
|
58
|
+
* turning off nginx, local -> playground: 146/s. Noteworthy: this is the only thing on playground that helped me go
|
|
59
|
+
faster. But we don't actually want to recommend that, of course. Also: this is the only setup where I was able to
|
|
60
|
+
outrun snappea (for a short while). Note that tuning thread for gunicorn / stress-test matters here. (I used 25)
|
|
61
|
+
|
|
62
|
+
* playground locally w/o nginx and w/ drop-to-floor: 400/s. Noteworthy: very close to what I get on my laptop.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Some conclusions:
|
|
67
|
+
|
|
68
|
+
* 30/s is still "a lot"; that's 2.5M/day or 77M/month, which is _more_ than the maximum Sentry allows you to select in
|
|
69
|
+
the pricing page. (50M maxes out at $5,795.50 prepaid per month)
|
|
70
|
+
|
|
71
|
+
* Still, the above raises some questions on "is snappea worth it in this setup". Counterpoints (stability,
|
|
72
|
+
predictability, the fact that there may be other slow async things) still apply.
|
|
73
|
+
|
|
74
|
+
* I never really got a chance to tune my setup. I did raise gunicorn workers to "enough to deal with the number of
|
|
75
|
+
threads" which was in the 16 - 32 range. But with snappea without a backlog the number of workers is not material to
|
|
76
|
+
the performance.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
### Snappea
|
|
2
|
+
|
|
3
|
+
I ditched Celery.
|
|
4
|
+
|
|
5
|
+
Why? It needs a broker. And the broker is "yet another thing to set up". Moving parts are the enemy!
|
|
6
|
+
There may still be a future scenario where celery is a choice one can make. I've kept the interface as similar to celery
|
|
7
|
+
as I could, so I can have it swapped back in later if needed.
|
|
8
|
+
|
|
9
|
+
Considered alternatives:
|
|
10
|
+
|
|
11
|
+
* redislite w/ celery
|
|
12
|
+
this seems "risky", you're basically putting an unsupported part in the (already complicated) machine and hope for
|
|
13
|
+
the best.
|
|
14
|
+
|
|
15
|
+
* huey with file or sqlite backend?
|
|
16
|
+
the documentation seems to steer one away from this.
|
|
17
|
+
|
|
18
|
+
* "inside" gunicorn: this didn't seem straightforward at all. uvicorn/channels may be better ways forward, but again,
|
|
19
|
+
the docs are steering one away from this.
|
|
20
|
+
|
|
21
|
+
For now I'm just running in always-eager mode to facilitate running debugserver. This will probably change at some point.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Considered alternatives for the "waking up"
|
|
26
|
+
|
|
27
|
+
Currently implemented using inotify.
|
|
28
|
+
Main drawback: tie-in to Linux.
|
|
29
|
+
|
|
30
|
+
However, the model of "just writing some unique file" is very simple to understand.
|
|
31
|
+
And the division of labor regarding these files is also simple (the clients just put files there, the snappeaserver just cleans)
|
|
32
|
+
And it's a nicely decoupled model too: if either server or client is temporarily gone, the messages are not lost/blocking ("postbox model")
|
|
33
|
+
And it provides a potential path forward if we ever want to put everything (the messages themselves) in those files.
|
|
34
|
+
|
|
35
|
+
* signals: abandoned, e.g. https://github.com/python/cpython/issues/118143 for the reason
|
|
36
|
+
|
|
37
|
+
* SIGSTP / SIGCONT: abandoned, I couldn't get this work reliably with "many signals" either. Process hangs mysteriously.
|
|
38
|
+
|
|
39
|
+
* busy loop with read-on-the-db: still a good candidate. However: the read on the DB is somewhere between .1ms and 1ms.
|
|
40
|
+
and appears to be CPU-intensive. This means that the sleep() in the loop becomes something to think about and
|
|
41
|
+
potentially even configure (boooh!). In particular, you get a trade-off between the latency of picking up the work,
|
|
42
|
+
and the CPU load of the busy loop, which may depend on the fastness of your platform. This is exactly the kind of
|
|
43
|
+
thinking that I want to take away from myusers (and myself).
|
|
44
|
+
|
|
45
|
+
* Zero MQ: still a strong candidate. I like that it's not tying us to Linux. And it's still brokerless. However, half
|
|
46
|
+
way through reading its manual I decided implementing this myself would still be faster than understanding yet another
|
|
47
|
+
component. Also: TCP is basically implied, which is another point of questions (firewalls etc).
|
|
48
|
+
|
|
49
|
+
* Python's multiprocessing.connection.Listener ... introduces pickling. Yuck!
|
|
50
|
+
|
|
51
|
+
* A FIFO where we write bytes: it doesn't seem to be a good fit for more than a single writer.
|
|
52
|
+
|
bugsink-0.1.12/DESIGN.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
## Goals
|
|
2
|
+
|
|
3
|
+
* Simplicity
|
|
4
|
+
* Stability: easy to deploy; deploy once, and then forget it.
|
|
5
|
+
|
|
6
|
+
Not "feature rich"
|
|
7
|
+
|
|
8
|
+
Get *notified* about *programming errors* (not: connection errors) as they occur.
|
|
9
|
+
Enough *context* to solve these errors.
|
|
10
|
+
|
|
11
|
+
On-prem running is the expected default.
|
|
12
|
+
|
|
13
|
+
They sent a monolith to catch a monolith.
|
|
14
|
+
|
|
15
|
+
Flowing from desire for "stability":
|
|
16
|
+
|
|
17
|
+
* quota-from-the-ground-up (because otherwise: accidental DDOS)
|
|
18
|
+
* thinking about resource-usage from-the-ground-up.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
CLI-first
|
|
22
|
+
|
|
23
|
+
proxying should be a primitive
|
|
24
|
+
i.e. local client should be similar to servers.
|
|
25
|
+
(passing events is natural)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
built from understood/known performance characteristics
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Solve as much client-side as possible.
|
|
32
|
+
|
|
33
|
+
What you care about: "issues", not "events"
|
|
34
|
+
* issue-grouping
|
|
35
|
+
* We don't make money per-issue, we don't want to know about re-occurrences (or we want to know only a little about them)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
How valuable is the data? do we care about throwing out info?
|
|
39
|
+
"not as valuable as a running system"
|
|
40
|
+
errors that you care about will likely re-occur.
|
|
41
|
+
still: it's nice to not lose information.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Splitting the issue-database from the organizational database?
|
|
45
|
+
"maybe"
|
|
46
|
+
con: extra complexity
|
|
47
|
+
pro: these things have different backup/reproduction regimes
|
|
48
|
+
the issues (other than "resolution") can be reconstructed from
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Event-based from the ground up.
|
|
53
|
+
because "duh"... this is a textbook event system
|
|
54
|
+
because stability: easier to reason about costs this way.
|
|
55
|
+
because easier to get performant: do your calculations incrementally (e.g. quota usage becomes a +=1 operation)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
TECHNICAL CHOICES:
|
|
60
|
+
|
|
61
|
+
mysql v.s. postgres (v.s. sqlite): make an _informed_ decision
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
NEXT UP
|
|
66
|
+
|
|
67
|
+
Start building
|
|
68
|
+
motto: off the shelve and boring components, but with an architecture which takes replacability in mind
|
|
69
|
+
|
|
70
|
+
ingesting first
|
|
71
|
+
as a simple Django app which dumps everything into a textfield (this is TSTTCPW)
|
|
72
|
+
|
|
73
|
+
this also allows for
|
|
74
|
+
|
|
75
|
+
dogfooding becomes possible:
|
|
76
|
+
turn on sentry-SDK and send issues your own way.
|
|
77
|
+
|
|
78
|
+
a bunch of example events are availabe in e.g. GlitchTip.
|
|
79
|
+
|
|
80
|
+
install both GlitchTip and Sentry locally.
|
|
81
|
+
optie 1: met de aangeraden docker-compose
|
|
82
|
+
optie 2: the "developer" view (misschien beter als je pdb wilt kunnen doen??)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
NEXT READING SESSION: last OS version of sentry itself.
|
|
86
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
ARG PYTHON_VERSION=3.12
|
|
2
|
+
|
|
3
|
+
# Build image: non-slim, in particular to build the mysqlclient wheel
|
|
4
|
+
FROM python:${PYTHON_VERSION} AS build
|
|
5
|
+
|
|
6
|
+
ARG WHEEL_FILE=wheelfile-not-specified.whoops
|
|
7
|
+
|
|
8
|
+
COPY dist/$WHEEL_FILE /wheels/
|
|
9
|
+
RUN --mount=type=cache,target=/var/cache/buildkit/pip \
|
|
10
|
+
pip wheel --wheel-dir /wheels /wheels/${WHEEL_FILE} mysqlclient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Actual image (based on slim)
|
|
14
|
+
FROM python:${PYTHON_VERSION}-slim
|
|
15
|
+
|
|
16
|
+
# ARGs are not inherited from the build stage; https://stackoverflow.com/a/56748289/339144
|
|
17
|
+
ARG WHEEL_FILE
|
|
18
|
+
ENV PYTHONUNBUFFERED=1
|
|
19
|
+
|
|
20
|
+
ENV PORT=8000
|
|
21
|
+
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
|
|
24
|
+
# mysqlclient dependencies; needed here too, because the built wheel depends on .o files
|
|
25
|
+
RUN apt update && apt install default-libmysqlclient-dev -y
|
|
26
|
+
|
|
27
|
+
COPY --from=build /wheels /wheels
|
|
28
|
+
RUN --mount=type=cache,target=/var/cache/buildkit/pip \
|
|
29
|
+
pip install --find-links /wheels --no-index /wheels/$WHEEL_FILE mysqlclient
|
|
30
|
+
|
|
31
|
+
COPY bugsink/conf_templates/docker.py.template bugsink_conf.py
|
|
32
|
+
|
|
33
|
+
RUN ["bugsink-manage", "migrate", "snappea", "--database=snappea"]
|
|
34
|
+
|
|
35
|
+
EXPOSE $PORT
|
|
36
|
+
|
|
37
|
+
CMD [ "monofy", "bugsink-manage", "check", "--deploy", "--fail-level", "WARNING", "&&", "bugsink-manage", "migrate", "&&", "gunicorn", "--bind=0.0.0.0:$PORT", "--workers=10", "--access-logfile", "-", "bugsink.wsgi", "|||", "bugsink-runsnappea"]
|
bugsink-0.1.12/LICENSE
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
With regard to the Bugsink Software:
|
|
2
|
+
|
|
3
|
+
This software and associated documentation files (the "Software") may only be
|
|
4
|
+
used under either of the following 2 conditions:
|
|
5
|
+
|
|
6
|
+
1. In production, if you (and any entity that you represent) have agreed to,
|
|
7
|
+
and are in compliance with, the Bugsink Subscription Terms of Service,
|
|
8
|
+
available at TODO (the "Terms"), or other agreement governing the use of the
|
|
9
|
+
Software, as agreed by you and Bugsink B.V., and otherwise have a valid
|
|
10
|
+
Bugsink subscription for the correct number of users.
|
|
11
|
+
|
|
12
|
+
OR
|
|
13
|
+
|
|
14
|
+
2. In a non-production environment, for a single user, for the purpose of
|
|
15
|
+
evaluating the Software, provided that you are not using or have not used
|
|
16
|
+
the Software in a way that would violate the Terms.
|
|
17
|
+
|
|
18
|
+
Subject to the foregoing condition, you are free to modify this Software,
|
|
19
|
+
provided that you do not remove the copyright notice and the license notice and
|
|
20
|
+
do not modify the code in a way that would conflict with the Terms.
|
|
21
|
+
|
|
22
|
+
You are not granted any other rights beyond what is expressly stated herein.
|
|
23
|
+
|
|
24
|
+
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute,
|
|
25
|
+
sublicense, and/or sell the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
+
SOFTWARE.
|
|
34
|
+
|
|
35
|
+
For all third party components incorporated into the Bugsink Software, those
|
|
36
|
+
components are licensed under the original license provided by the owner of the
|
|
37
|
+
applicable component:
|
|
38
|
+
|
|
39
|
+
* The icons in SVG format, directly included in various html templates are from
|
|
40
|
+
Heroicons by Tailwind Labs, Inc. (MIT License)
|
|
41
|
+
|
|
42
|
+
* The code in the 'sentry' directory is Copyright 2019 Sentry
|
|
43
|
+
(https://sentry.io) and individual contributors. (BSD 3-Clause License)
|
|
44
|
+
|
|
45
|
+
* Your package manager (typically pip) will install additional third party
|
|
46
|
+
components upon installing Bugsink. Please refer to the license of the
|
|
47
|
+
respective component in your package manager's repository.
|