-
Notifications
You must be signed in to change notification settings - Fork 29
Add Kafka download metrics #1191
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
base: master
Are you sure you want to change the base?
Changes from 10 commits
aec8325
7722107
640aa2f
9a1af22
78d0d77
ed8b6e4
af640d4
deb2cfc
0ae1e8e
b9c2462
9d639c1
0756233
38b5997
75c80d8
eb036a9
9d5f410
c8c2894
c70964e
a5098bf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
x753 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| default_app_config = "thunderstore.ts_analytics.apps.AnalyticsAppConfig" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| from django.apps import AppConfig | ||
| from django.db.models.signals import post_save | ||
|
|
||
|
|
||
| class AnalyticsAppConfig(AppConfig): | ||
| name = "thunderstore.ts_analytics" | ||
| label = "ts_analytics" | ||
|
|
||
| def ready(self): | ||
| # Connect the signal handlers | ||
| from thunderstore.community.models import Community, PackageListing | ||
| from thunderstore.metrics.models import PackageVersionDownloadEvent | ||
| from thunderstore.repository.models import Package, PackageVersion | ||
| from thunderstore.ts_analytics.signals import ( | ||
| community_post_save, | ||
| package_listing_post_save, | ||
| package_post_save, | ||
| package_version_download_event_post_save, | ||
| package_version_post_save, | ||
| ) | ||
|
|
||
| post_save.connect( | ||
| package_version_download_event_post_save, sender=PackageVersionDownloadEvent | ||
| ) | ||
| post_save.connect(package_post_save, sender=Package) | ||
| post_save.connect(package_version_post_save, sender=PackageVersion) | ||
| post_save.connect(package_listing_post_save, sender=PackageListing) | ||
| post_save.connect(community_post_save, sender=Community) | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import json | ||
| from enum import Enum | ||
| from functools import lru_cache | ||
| from typing import Any, Dict, Optional, Union | ||
|
|
||
| from celery import shared_task | ||
| from confluent_kafka import Producer | ||
| from django.conf import settings | ||
|
|
||
| from thunderstore.core.settings import CeleryQueues | ||
|
|
||
|
|
||
| class KafkaTopic(str, Enum): | ||
| PACKAGE_DOWNLOADED = "ts.package.downloaded" | ||
| PACKAGE_UPDATED = "ts.package.updated" | ||
| PACKAGE_VERSION_UPDATED = "ts.package.version.updated" | ||
| PACKAGE_LISTING_UPDATED = "ts.package.listing.updated" | ||
| COMMUNITY_UPDATED = "ts.community.updated" | ||
|
|
||
|
|
||
| def send_kafka_message( | ||
| topic: Union[KafkaTopic, str], payload: dict, key: Optional[str] = None | ||
| ): | ||
| client = get_kafka_client() | ||
| client.send(topic=topic, payload=payload, key=key) | ||
|
|
||
|
|
||
| @shared_task( | ||
| queue=CeleryQueues.Analytics, | ||
| name="thunderstore.analytics.send_kafka_message_async", | ||
| ) | ||
| def send_kafka_message_async( | ||
| topic: Union[KafkaTopic, str], payload: dict, key: Optional[str] = None | ||
| ): | ||
| send_kafka_message(topic=topic, payload=payload, key=key) | ||
x753 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| class KafkaClient: | ||
| def __init__(self, config: Dict[str, Any]): | ||
| self._producer = Producer(config) | ||
|
|
||
| def send( | ||
| self, | ||
| topic: Union[KafkaTopic, str], | ||
| payload: dict, | ||
| key: Optional[str] = None, | ||
| ): | ||
| value_bytes = json.dumps(payload).encode("utf-8") | ||
| key_bytes = key.encode("utf-8") if key else None | ||
|
|
||
| topic_str = topic.value if isinstance(topic, KafkaTopic) else topic | ||
| self._producer.produce( | ||
| topic=topic_str, | ||
| value=value_bytes, | ||
| key=key_bytes, | ||
| ) | ||
x753 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| self._producer.poll(0) | ||
|
|
||
x753 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| class DummyKafkaClient: | ||
| """A dummy Kafka client that does nothing when Kafka is disabled.""" | ||
|
|
||
| def send( | ||
| self, topic: Union[KafkaTopic, str], payload: dict, key: Optional[str] = None | ||
| ): | ||
| pass | ||
|
|
||
|
|
||
| @lru_cache(maxsize=1) | ||
| def get_kafka_client() -> Union[KafkaClient, DummyKafkaClient]: | ||
| # Return dummy client if Kafka is disabled | ||
| if not getattr(settings, "KAFKA_ENABLED", False): | ||
| return DummyKafkaClient() | ||
|
|
||
| config = getattr(settings, "KAFKA_CONFIG", None) | ||
| if not config: | ||
| raise RuntimeError("KAFKA_CONFIG is not configured.") | ||
|
|
||
| if not config.get("bootstrap.servers"): | ||
| raise RuntimeError("Kafka bootstrap servers are not configured.") | ||
|
|
||
| return KafkaClient(config) | ||
x753 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
Uh oh!
There was an error while loading. Please reload this page.