diff --git a/Procfile b/Procfile index 2dc630d1d..d8c93c06f 100644 --- a/Procfile +++ b/Procfile @@ -4,4 +4,4 @@ web: gunicorn --config gunicorn.conf.py gettingstarted.wsgi # migrations are run as part of app deployment, using Heroku's Release Phase feature: # https://docs.djangoproject.com/en/5.2/topics/migrations/ # https://devcenter.heroku.com/articles/release-phase -#release: ./manage.py migrate --no-input +release: ./manage.py migrate --no-input diff --git a/gettingstarted/urls.py b/gettingstarted/urls.py index b35f43dbb..e6a322c4a 100644 --- a/gettingstarted/urls.py +++ b/gettingstarted/urls.py @@ -23,6 +23,11 @@ urlpatterns = [ path("", hello.views.index, name="index"), path("db/", hello.views.db, name="db"), + path("ao-custom/", hello.views.ao_custom, name="ao_custom"), + path("sleep/", hello.views.sleep, name="sleep"), + path("status/", hello.views.status, name="status"), + path("resource/", hello.views.resource, name="resource"), + path("logs/", hello.views.logs, name="logs"), # Uncomment this and the entry in `INSTALLED_APPS` if you wish to use the Django admin feature: # https://docs.djangoproject.com/en/5.2/ref/contrib/admin/ # path("admin/", admin.site.urls), diff --git a/hello/views.py b/hello/views.py index bd7a34b00..9500b8e1c 100644 --- a/hello/views.py +++ b/hello/views.py @@ -1,3 +1,8 @@ +import random +import threading +import time + +from django.http import HttpResponse from django.shortcuts import render from .models import Greeting @@ -8,6 +13,21 @@ def index(request): return render(request, "index.html") +def ao_custom(request): + # counts + print("count#user.clicks=1") + print("count#user.clicks.tagged=1 tag#user_id=2 tag#user_geo=earth") + print("source=us-west count#user.clicks.sourced=1") + # measures + print("measure#database.query=200ms") + print("measure#database.query.tagged=200ms tag#db_name=foo tag#db_type=postgres") + print("source=us-east measure#database.query.sourced=200ms") + # samples + print("sample#database.size=40.9MB") + print("sample#database.size.tagged=40.9MB tag#db_name=foo tag#db_type=postgres") + print("source=another-source sample#database.size.sourced=40.9MB") + return render(request, "index.html") + def db(request): # If you encounter errors visiting the `/db/` page on the example app, check that: @@ -26,3 +46,81 @@ def db(request): greetings = Greeting.objects.all() return render(request, "db.html", {"greetings": greetings}) + +def sleep(request): + # sleep for query parameter `ms` milliseconds, or random duration up to 3s if not specified. + try: + sleep_s = int(request.GET["ms"]) / 1000 + except (KeyError, ValueError): + sleep_s = random.uniform(0, 3.0) + print(f"sleeping {sleep_s}s") + time.sleep(sleep_s) + return HttpResponse(f"slept {sleep_s} seconds", content_type="text/plain") + +def status(request): + # return query parameter `code` as status code, or 400 if not specified. + # throw unhandled exception on invalid value. + status_code = int(request.GET.get("code", 400)) + if status_code < 100 or status_code > 599: + raise ValueError("status code invalid range") + return HttpResponse(f"status {status_code}", content_type="text/plain", status=status_code) + +def resource(request): + # simulate memory allocation and cpu load. + # get size in KB and thread count from query parameters `kb` and `threads`. + try: + size_kb = int(request.GET["kb"]) * 1024 + except (KeyError, ValueError): + size_kb = 10 * 1024 + + try: + threads_count = int(request.GET["threads"]) + except (KeyError, ValueError): + threads_count = 16 + + def memory_and_cpu_load(tid, memory_size_kb): + data = bytearray(memory_size_kb * 1024) + end_time = time.time() + 5 # run for 5 seconds + iterations = 0 + while time.time() < end_time: + _ = sum(i ** 2 for i in range(1000)) + iterations += 1 + info[tid] = iterations + del data + + info = {} + threads = [] + for i in range(threads_count): + thread = threading.Thread(target=memory_and_cpu_load, args=(i, size_kb,)) + threads.append(thread) + thread.start() + for thread in threads: + thread.join() + + return HttpResponse(str(info), content_type="text/plain") + +def logs(request): + # log a burst of messages based on query parameters `count` and `threads`, + # or 1024 messages each from 16 threads if not specified. + try: + msg_count = int(request.GET["count"]) + except (KeyError, ValueError): + msg_count = 1024 + try: + threads_count = int(request.GET["threads"]) + except (KeyError, ValueError): + threads_count = 16 + + def log_burst(tid, count): + for i in range(count): + print(f"log burst tid: {tid} message: {i}") + + threads = [] + for i in range(threads_count): + thread = threading.Thread(target=log_burst, args=(i, msg_count,)) + threads.append(thread) + thread.start() + for thread in threads: + thread.join() + + return HttpResponse(f"{threads_count} threads each logged {msg_count} messages", content_type="text/plain") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index d07820d18..28ec9ae2b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ dj-database-url>=3,<4 whitenoise>=6,<7 # Uncomment to use a Postgres database. -#psycopg[binary] +psycopg[binary]