From 60a9ea12617e9b17c6f5d69fd55578879fed8a04 Mon Sep 17 00:00:00 2001 From: vitchenkokir Date: Tue, 14 Jul 2026 16:57:26 +0300 Subject: [PATCH 1/4] refactor: restructure questions and update related files --- .qwen/settings.json | 55 ++++ .qwen/settings.json.orig | 54 ++++ app/ai/faster_whisper_transcriber.py | 17 +- app/coding/api/ws_session.py | 87 ++++- app/theory/services/submission.py | 18 ++ data/coding/python/junior/kafka.yaml | 78 +++++ data/coding/python/junior/rabbitmq.yaml | 76 +++++ data/coding/python/middle/kafka.yaml | 163 ++++++++++ data/coding/python/middle/rabbitmq.yaml | 157 +++++++++ data/coding/python/senior/kafka.yaml | 244 ++++++++++++++ data/coding/python/senior/rabbitmq.yaml | 217 +++++++++++++ data/config.json.bak | 7 + data/config.json.tmp | 7 + data/llm_models.json.bak | 14 + .../airflow/junior/configuration.yaml | 45 +-- .../airflow/junior/fundamentals.yaml | 17 +- data/questions/airflow/middle/executors.yaml | 6 +- data/questions/airflow/middle/operations.yaml | 53 +--- data/questions/airflow/middle/scheduling.yaml | 10 +- data/questions/airflow/middle/taskflow.yaml | 9 +- data/questions/airflow/senior/production.yaml | 25 +- .../database/junior/design-basics.yaml | 8 +- .../database/junior/mysql-basics.yaml | 20 +- .../database/junior/redis-basics.yaml | 18 +- .../questions/database/junior/sql-basics.yaml | 28 +- .../database/junior/sqlite-basics.yaml | 11 +- .../database/middle/locking-concurrency.yaml | 11 +- .../questions/database/middle/migrations.yaml | 7 +- .../questions/database/middle/postgresql.yaml | 7 +- data/questions/database/middle/redis.yaml | 18 +- .../database/middle/sql-advanced.yaml | 31 +- .../database/senior/clickhouse-analytics.yaml | 15 +- .../questions/docker/junior/fundamentals.yaml | 69 +--- data/questions/docker/middle/operations.yaml | 33 +- data/questions/docker/senior/security.yaml | 23 +- data/questions/kafka/junior/fundamentals.yaml | 23 +- data/questions/kafka/middle/messaging.yaml | 43 +-- data/questions/kafka/middle/storage.yaml | 18 +- data/questions/kafka/senior/architecture.yaml | 28 +- .../kubernetes/junior/fundamentals.yaml | 22 +- .../kubernetes/middle/networking.yaml | 10 +- .../kubernetes/middle/scheduling.yaml | 13 +- .../kubernetes/senior/production.yaml | 12 +- .../observability/junior/logging.yaml | 13 +- ...metheus.yaml => prometheus_questions.yaml} | 35 +-- .../observability/junior/visualization.yaml | 57 +--- .../observability/middle/logging.yaml | 49 +-- ...metheus.yaml => prometheus_questions.yaml} | 47 +-- .../observability/middle/visualization.yaml | 15 +- .../observability/senior/logging.yaml | 6 +- ...metheus.yaml => prometheus_questions.yaml} | 12 +- data/questions/python/junior/async.yaml | 297 +----------------- data/questions/python/junior/basics.yaml | 76 +---- .../questions/python/junior/control-flow.yaml | 10 +- .../python/junior/data-structures.yaml | 17 +- data/questions/python/junior/django-drf.yaml | 38 +-- data/questions/python/junior/django.yaml | 29 +- data/questions/python/junior/exceptions.yaml | 21 +- data/questions/python/junior/fastapi.yaml | 47 +-- data/questions/python/junior/functions.yaml | 36 +-- data/questions/python/junior/kafka.yaml | 22 +- data/questions/python/junior/oop.yaml | 61 +--- data/questions/python/junior/pytest.yaml | 34 +- data/questions/python/junior/rabbitmq.yaml | 41 +-- data/questions/python/junior/strings.yaml | 23 +- .../questions/python/middle/architecture.yaml | 11 +- data/questions/python/middle/asyncio.yaml | 37 +-- data/questions/python/middle/django-drf.yaml | 30 +- data/questions/python/middle/django.yaml | 31 +- data/questions/python/middle/fastapi.yaml | 32 +- data/questions/python/middle/kafka.yaml | 45 +-- .../python/middle/metaprogramming.yaml | 43 +-- data/questions/python/middle/pytest.yaml | 36 +-- data/questions/python/middle/rabbitmq.yaml | 55 +--- data/questions/python/middle/type-hints.yaml | 41 +-- data/questions/python/senior/django.yaml | 11 +- data/questions/python/senior/fastapi.yaml | 3 +- data/questions/python/senior/kafka.yaml | 67 ++-- data/questions/python/senior/performance.yaml | 7 +- data/questions/python/senior/rabbitmq.yaml | 48 +-- data/questions/questions_map.yaml | 50 +-- .../rabbitmq/junior/fundamentals.yaml | 39 +-- data/questions/rabbitmq/middle/messaging.yaml | 14 +- .../rabbitmq/senior/architecture.yaml | 56 +--- .../questions/rabbitmq/senior/operations.yaml | 23 +- pyproject.toml | 1 + static/js/coding_complete.js | 147 +++++++++ static/js/coding_session.js | 20 ++ static/js/interview_audio_answer.js | 7 +- static/js/interview_timer.js | 14 +- templates/coding_interview.html | 118 +------ templates/interview.html | 20 +- tests/speech/services/test_dictation.py | 24 -- uv.lock | 104 +++++- 94 files changed, 1769 insertions(+), 2178 deletions(-) create mode 100644 .qwen/settings.json create mode 100644 .qwen/settings.json.orig create mode 100644 data/coding/python/junior/kafka.yaml create mode 100644 data/coding/python/junior/rabbitmq.yaml create mode 100644 data/coding/python/middle/kafka.yaml create mode 100644 data/coding/python/middle/rabbitmq.yaml create mode 100644 data/coding/python/senior/kafka.yaml create mode 100644 data/coding/python/senior/rabbitmq.yaml create mode 100644 data/config.json.bak create mode 100644 data/config.json.tmp create mode 100644 data/llm_models.json.bak rename data/questions/observability/junior/{prometheus.yaml => prometheus_questions.yaml} (75%) rename data/questions/observability/middle/{prometheus.yaml => prometheus_questions.yaml} (67%) rename data/questions/observability/senior/{prometheus.yaml => prometheus_questions.yaml} (73%) create mode 100644 static/js/coding_complete.js diff --git a/.qwen/settings.json b/.qwen/settings.json new file mode 100644 index 0000000..a32fff2 --- /dev/null +++ b/.qwen/settings.json @@ -0,0 +1,55 @@ +{ + "permissions": { + "allow": [ + "Agent(Explore)", + "Skill(qc-helper)", + "Bash(python *)", + "Bash(pytest *)", + "Bash(uv *)", + "Bash(find *)", + "Agent(general-purpose)", + "Bash(sleep *)", + "Bash(do *)", + "Bash(done)", + "Bash(python3 *)", + "Bash(ruff *)", + "Bash(\"\"\"debug submitted_code persistence.\"\"\")", + "Bash(from *)", + "Bash(engine *)", + "Bash(\"sqlite:///:memory:\",)", + "Bash(false},)", + "Bash(base.metadata.create_all)", + "Bash(session_factory *)", + "Bash(db_module.sessionlocal *)", + "Bash(session *)", + "Bash(interview *)", + "Bash(trackselection)", + "Bash(session.add)", + "Bash(session.flush)", + "Bash(section *)", + "Bash(session, *)", + "Bash(coding_sel *)", + "Bash(.coding_creation)", + "Bash(section.selection_spec *)", + "Bash(coding_sel)", + "Bash(tasks *)", + "Bash(session.commit)", + "Bash(task_id *)", + "Bash(print)", + "Bash(uow *)", + "Bash(section_agg *)", + "Bash(\"debug-1\")", + "Bash(updated *)", + "Bash(task_id,)", + "Bash(uow.coding_sections.save_aggregate)", + "Bash(uow.flush)", + "Bash(uow.commit)", + "Bash(uow.close)", + "Bash(uow2 *)", + "Bash(section2 *)", + "Bash(uow2.close)", + "Bash(pyeof)" + ] + }, + "$version": 4 +} \ No newline at end of file diff --git a/.qwen/settings.json.orig b/.qwen/settings.json.orig new file mode 100644 index 0000000..7338d3a --- /dev/null +++ b/.qwen/settings.json.orig @@ -0,0 +1,54 @@ +{ + "permissions": { + "allow": [ + "Agent(Explore)", + "Skill(qc-helper)", + "Bash(python *)", + "Bash(pytest *)", + "Bash(uv *)", + "Bash(find *)", + "Agent(general-purpose)", + "Bash(sleep *)", + "Bash(do *)", + "Bash(done)", + "Bash(python3 *)", + "Bash(ruff *)", + "Bash(\"\"\"debug submitted_code persistence.\"\"\")", + "Bash(from *)", + "Bash(engine *)", + "Bash(\"sqlite:///:memory:\",)", + "Bash(false},)", + "Bash(base.metadata.create_all)", + "Bash(session_factory *)", + "Bash(db_module.sessionlocal *)", + "Bash(session *)", + "Bash(interview *)", + "Bash(trackselection)", + "Bash(session.add)", + "Bash(session.flush)", + "Bash(section *)", + "Bash(session, *)", + "Bash(coding_sel *)", + "Bash(.coding_creation)", + "Bash(section.selection_spec *)", + "Bash(coding_sel)", + "Bash(tasks *)", + "Bash(session.commit)", + "Bash(task_id *)", + "Bash(print)", + "Bash(uow *)", + "Bash(section_agg *)", + "Bash(\"debug-1\")", + "Bash(updated *)", + "Bash(task_id,)", + "Bash(uow.coding_sections.save_aggregate)", + "Bash(uow.flush)", + "Bash(uow.commit)", + "Bash(uow.close)", + "Bash(uow2 *)", + "Bash(section2 *)", + "Bash(uow2.close)" + ] + }, + "$version": 4 +} \ No newline at end of file diff --git a/app/ai/faster_whisper_transcriber.py b/app/ai/faster_whisper_transcriber.py index 458e059..bfcf377 100644 --- a/app/ai/faster_whisper_transcriber.py +++ b/app/ai/faster_whisper_transcriber.py @@ -3,6 +3,7 @@ """faster-whisper implementation of :class:`~app.ai.speech_transcriber.SpeechTranscriber`.""" import asyncio +import logging from faster_whisper import WhisperModel import numpy as np @@ -10,6 +11,8 @@ from app.shared.locales import normalize_locale +logger = logging.getLogger(__name__) + class FasterWhisperTranscriber: """Transcribe audio using an in-memory ``WhisperModel``.""" @@ -39,12 +42,20 @@ async def transcribe( language = normalize_locale(locale) def _transcribe() -> str: - segments, _info = self._model.transcribe( + segments, info = self._model.transcribe( audio, language=language, task="transcribe", - vad_filter=True, + vad_filter=False, + ) + segment_list = list(segments) + result = "".join((segment.text or "") for segment in segment_list).strip() + logger.info( + "Whisper transcript: language=%s segments=%d result=%r", + info.language, + len(segment_list), + result, ) - return "".join(segment.text for segment in segments).strip() + return result return await asyncio.to_thread(_transcribe) diff --git a/app/coding/api/ws_session.py b/app/coding/api/ws_session.py index 704454d..29685ba 100644 --- a/app/coding/api/ws_session.py +++ b/app/coding/api/ws_session.py @@ -3,13 +3,18 @@ """WebSocket message handling for coding sessions.""" from collections.abc import AsyncIterator +from dataclasses import replace import logging from typing import Any from app.ai.base import AIProvider from app.coding.api.errors import coding_ws_error_payload from app.coding.api.ws_protocol import coding_event_to_message -from app.coding.domain.exceptions import CodingDomainError +from app.coding.domain.entities import CodingTask +from app.coding.domain.exceptions import CodingDomainError, CodingSectionNotFoundError +from app.coding.repositories.uow import CodingUnitOfWork +from app.coding.services.events import CodingFeedbackEvent +from app.coding.services.navigation import CodingNavigationService from app.coding.services.submission import CodingSubmissionService from app.interview.domain.exceptions import InterviewDomainError from app.interview.services.ai_errors import ai_error_message_for_client @@ -50,6 +55,14 @@ async def iter_responses( yield message return + if msg_type == "timeout": + async for message in CodingWebSocketService._handle_timeout( + raw, + interview_id=interview_id, + ): + yield message + return + yield { "type": "error", "message": f"Unknown message type: {msg_type}", @@ -88,3 +101,75 @@ async def _handle_submit( "type": "error", "message": ai_error_message_for_client(exc), } + + @staticmethod + async def _handle_timeout( + raw: dict[str, Any], + *, + interview_id: str, + ) -> AsyncIterator[dict[str, Any]]: + task_id = str(raw.get("task_id", "")).strip() + if not task_id: + yield { + "type": "error", + "message": "task_id is required", + } + return + + try: + with CodingUnitOfWork(auto_commit=True) as uow: + section = uow.coding_sections.get_aggregate(interview_id) + if section is None: + raise CodingSectionNotFoundError(interview_id) + section.ensure_active() + current = section.require_current_task(task_id) + round_num = current.round + order = current.order + + # Mark the current task as timed out: score 0, no feedback + tasks = tuple( + replace( + task, + score=0, + feedback="Time expired", + submitted_code=CodingTask.TIME_EXPIRED_CODE, + submit_test_summary=None, + ) + if task.id == current.id + else task + for task in section.tasks + ) + updated = replace(section, tasks=tasks) + uow.coding_sections.save_aggregate(updated) + + # Advance to the next unsubmitted task + next_task_data, timer_remaining = ( + CodingNavigationService.advance_to_next_unsubmitted( + uow, + interview_id, + task_id=task_id, + round_num=round_num, + ) + ) + + yield coding_event_to_message( + CodingFeedbackEvent( + task_id=task_id, + order=order, + round=round_num, + follow_up_needed=False, + follow_up_text=None, + follow_up_mode=None, + next_task=next_task_data, + feedback="Time expired", + timer_remaining_seconds=timer_remaining, + ) + ) + except (InterviewDomainError, CodingDomainError) as exc: + yield coding_ws_error_payload(exc) + except Exception as exc: + logger.exception("Coding timeout failed for interview %s", interview_id) + yield { + "type": "error", + "message": ai_error_message_for_client(exc), + } diff --git a/app/theory/services/submission.py b/app/theory/services/submission.py index 92d700c..98c4436 100644 --- a/app/theory/services/submission.py +++ b/app/theory/services/submission.py @@ -269,8 +269,15 @@ async def _transcribe_and_persist( Returns: Final transcript text (may be empty). """ + logger.info( + "[AudioAnswer] Starting transcription for interview=%s question=%s round=%d", + interview_id, + question_id, + round_num, + ) samples = wav_bytes_to_float32(wav_bytes) transcript = await transcriber.transcribe(samples, locale) + logger.info("[AudioAnswer] Transcription result: %r", transcript) with TheoryUnitOfWork(auto_commit=True) as uow: section = uow.theory_sections.get_aggregate(interview_id) if section is None: @@ -278,6 +285,7 @@ async def _transcribe_and_persist( current = section.find_task(question_id, round_num) updated = section.with_task_text(current.id, transcript) uow.theory_sections.save_aggregate(updated) + logger.info("[AudioAnswer] Persisted transcript to DB") return transcript @staticmethod @@ -469,6 +477,11 @@ async def stream_audio_answer_submission( """ TheorySubmissionService.require_audio_answer_enabled() validate_wav_bytes(wav_bytes) + logger.info( + "[AudioAnswer] Starting audio submission for interview=%s question=%s", + interview_id, + question_id, + ) ctx: TheorySubmissionContext | None = None async for item in TheorySubmissionService._open_submission( @@ -477,10 +490,15 @@ async def stream_audio_answer_submission( if isinstance(item, TheorySubmissionContext): ctx = item break + logger.info( + "[AudioAnswer] Yielding pre-context event: %s", type(item).__name__ + ) yield item if ctx is None: + logger.warning("[AudioAnswer] No submission context returned") return + logger.info("[AudioAnswer] Context opened, round=%d", ctx.round_num) yield AnswerSavedEvent() if ctx.round_num >= TheoryEvaluatorService.MAX_FOLLOW_UP_DEPTH: diff --git a/data/coding/python/junior/kafka.yaml b/data/coding/python/junior/kafka.yaml new file mode 100644 index 0000000..d99f5d1 --- /dev/null +++ b/data/coding/python/junior/kafka.yaml @@ -0,0 +1,78 @@ +category: "Kafka Client" +track: "python" +level: "junior" + +description: "Implement a Kafka producer in Python with JSON serialization, delivery callbacks, and async sending" + +tasks: + - id: "kafka-j-001" + difficulty: 2 + tags: ["kafka", "producer", "json", "serialization", "confluent-kafka", "callbacks"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a Kafka producer that sends JSON messages to a topic + and handles delivery reports. The producer should use confluent-kafka-python. + + Your task: + Complete the `produce` method so that it: + - Serializes the `data` dict to JSON (bytes) + - Sends the message with the given `key` + - Registers the provided `delivery_callback` for delivery confirmation + - Calls `poll(0)` after `produce()` to trigger callbacks + + The `flush` method is already implemented. + + Keep the existing producer config as-is. + ru: | + Контекст: + Вам нужно реализовать Kafka продюсера, который отправляет JSON-сообщения + и обрабатывает отчёты о доставке. Используется confluent-kafka-python. + + Задача: + Реализуйте метод `produce` так, чтобы он: + - Сериализовал `data` (dict) в JSON (bytes) + - Отправлял сообщение с указанным `key` + - Регистрировал `delivery_callback` для подтверждения доставки + - Вызывал `poll(0)` после `produce()` + + Метод `flush` уже реализован. Конфигурацию продюсера не меняйте. + starter_code: | + import json + from confluent_kafka import Producer + + + class JsonProducer: + def __init__(self, bootstrap_servers="localhost:9092"): + self.conf = { + "bootstrap.servers": bootstrap_servers, + "client.id": "py-producer", + } + self.producer = Producer(self.conf) + + def produce(self, topic, key, data, delivery_callback): + # TODO: serialize data to JSON bytes, produce with callback, poll(0) + pass + + def flush(self, timeout=None): + self.producer.flush(timeout) + + + def delivery_report(err, msg): + if err: + print(f"Failed: {err}") + else: + print(f"Delivered to {msg.topic()}[{msg.partition()}] @ {msg.offset()}") + + + producer = JsonProducer() + producer.produce("events", key="alice", data={"user": "alice", "action": "login"}, delivery_callback=delivery_report) + producer.flush() + expected_points: + - "Serializes data dict to JSON bytes with json.dumps().encode()" + - "Calls producer.produce() with topic, key, value, and callback" + - "Calls producer.poll(0) after produce() to trigger callbacks" + - "flush() is separate and handles final delivery wait" \ No newline at end of file diff --git a/data/coding/python/junior/rabbitmq.yaml b/data/coding/python/junior/rabbitmq.yaml new file mode 100644 index 0000000..c903cfd --- /dev/null +++ b/data/coding/python/junior/rabbitmq.yaml @@ -0,0 +1,76 @@ +category: "RabbitMQ Client" +track: "python" +level: "junior" + +description: "Implement a RabbitMQ producer in Python with publisher confirms, mandatory flag, and message persistence" + +tasks: + - id: "rabbitmq-j-001" + difficulty: 2 + tags: ["rabbitmq", "pika", "producer", "confirms", "mandatory", "persistence"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a RabbitMQ producer that sends messages with + publisher confirms, mandatory flag, and message persistence. + + Your task: + Complete the `publish` method so that it: + - Enables publisher confirms on the channel + - Declares the exchange as durable topic exchange + - Publishes the message with mandatory=True and persistent delivery_mode=2 + - Returns True if the message was confirmed, False otherwise + + Use `channel.confirm_delivery()` which returns True/False on publish. + The connection setup is already done. + ru: | + Контекст: + Вам нужно реализовать RabbitMQ продюсера с подтверждениями издателя, + флагом mandatory и персистентностью сообщений. + + Задача: + Реализуйте метод `publish` так, чтобы он: + - Включал подтверждения канала (publisher confirms) + - Объявлял exchange как durable topic exchange + - Публиковал сообщение с mandatory=True и delivery_mode=2 + - Возвращал True, если сообщение подтверждено, иначе False + + Используйте `channel.confirm_delivery()` — возвращает True/False. + Подключение к RabbitMQ уже настроено. + starter_code: | + import pika + from pika.spec import BasicProperties + + + class RabbitProducer: + def __init__(self, host="localhost"): + self.connection = pika.BlockingConnection( + pika.ConnectionParameters(host) + ) + self.channel = self.connection.channel() + + def publish(self, exchange, routing_key, body): + """Publish a persistent message with mandatory flag and publisher confirms. + + Returns True if confirmed, False otherwise. + """ + # TODO: enable confirms, declare exchange, publish with mandatory=True and delivery_mode=2 + pass + + def close(self): + self.connection.close() + + + producer = RabbitProducer() + success = producer.publish("my_exchange", "key", b"Hello, World!") + print(f"Message confirmed: {success}") + producer.close() + expected_points: + - "Calls channel.confirm_delivery() before publishing" + - "Declares exchange as durable topic exchange" + - "Publishes with mandatory=True" + - "Uses BasicProperties with delivery_mode=2 for persistence" + - "Returns the result of confirm_delivery-based publish" \ No newline at end of file diff --git a/data/coding/python/middle/kafka.yaml b/data/coding/python/middle/kafka.yaml new file mode 100644 index 0000000..832710d --- /dev/null +++ b/data/coding/python/middle/kafka.yaml @@ -0,0 +1,163 @@ +category: "Kafka Client Development" +track: "python" +level: "middle" + +description: "Implement Kafka consumers with manual offset commit and async consumption with aiokafka" + +tasks: + - id: "kafka-m-001" + difficulty: 3 + tags: ["kafka", "consumer", "manual-commit", "offset", "confluent-kafka", "error-handling"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a Kafka consumer with manual offset commit. + The consumer should read messages, process them, and commit offsets + only after successful processing. + + Your task: + Complete the `consume` method so that it: + - Subscribes to the given topic + - Polls messages in a loop (timeout=1.0) + - Skips empty messages (poll returns None) + - Logs and skips messages with errors + - Processes the message (calls process_msg) + - Commits the offset synchronously after successful processing + - Handles KeyboardInterrupt gracefully and closes the consumer + ru: | + Контекст: + Вам нужно реализовать Kafka консьюмера с ручным коммитом офсетов. + + Задача: + Реализуйте метод `consume` так, чтобы он: + - Подписывался на указанный топик + - Получал сообщения в цикле (timeout=1.0) + - Пропускал пустые сообщения (poll вернул None) + - Логировал и пропускал сообщения с ошибками + - Обрабатывал сообщение (вызов process_msg) + - Коммитил офсет синхронно после успешной обработки + - Обрабатывал KeyboardInterrupt и закрывал consumer + starter_code: | + import logging + from confluent_kafka import Consumer + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + + def process_msg(msg): + """Simulate message processing (may raise).""" + logger.info(f"Processing: {msg.value()}") + + + class ManualCommitConsumer: + def __init__(self, bootstrap_servers, group_id): + self.conf = { + "bootstrap.servers": bootstrap_servers, + "group.id": group_id, + "auto.offset.reset": "earliest", + "enable.auto.commit": False, + } + self.consumer = Consumer(self.conf) + + def consume(self, topic): + """Subscribe and consume messages with manual commit.""" + # TODO: subscribe, poll loop, process, commit sync + pass + + def close(self): + self.consumer.close() + + + consumer = ManualCommitConsumer("localhost:9092", "mygroup") + try: + consumer.consume("mytopic") + except KeyboardInterrupt: + logger.info("Shutting down...") + finally: + consumer.close() + expected_points: + - "Subscribes to the topic" + - "Polls messages with timeout=1.0" + - "Skips None results and messages with errors" + - "Calls process_msg and commits synchronously after processing" + - "Calls consumer.close() on shutdown" + + - id: "kafka-m-002" + difficulty: 3 + tags: ["kafka", "aiokafka", "asyncio", "fastapi", "consumer", "startup-shutdown"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to integrate an async Kafka consumer (aiokafka) into a FastAPI application. + The consumer should start on application startup and stop on shutdown. + + Your task: + Complete the `consume_loop` coroutine so that it: + - Iterates over messages from the consumer + - Processes each message via `process_message` + - Commits the offset manually after processing + + The FastAPI app, consumer setup, and startup/shutdown events are already in place. + Use `async for msg in consumer:` for iteration. + ru: | + Контекст: + Нужно интегрировать асинхронный Kafka консьюмер (aiokafka) в FastAPI приложение. + + Задача: + Реализуйте корутину `consume_loop` так, чтобы она: + - Итерировалась по сообщениям от consumer + - Обрабатывала каждое сообщение через `process_message` + - Коммитила офсет после обработки + + FastAPI приложение, consumer и события startup/shutdown уже настроены. + Используйте `async for msg in consumer:` для итерации. + starter_code: | + from fastapi import FastAPI + from aiokafka import AIOKafkaConsumer + import asyncio + import logging + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + app = FastAPI() + consumer = AIOKafkaConsumer( + "mytopic", + bootstrap_servers="localhost:9092", + group_id="fastapi-group", + ) + + + async def process_message(msg): + """Simulate message processing.""" + logger.info(f"Processed: {msg.value}") + await asyncio.sleep(0.1) + + + async def consume_loop(): + """Consume messages in a loop.""" + # TODO: iterate over consumer, process each message, commit + pass + + + @app.on_event("startup") + async def startup(): + await consumer.start() + asyncio.create_task(consume_loop()) + + + @app.on_event("shutdown") + async def shutdown(): + await consumer.stop() + expected_points: + - "Uses async for msg in consumer: to iterate" + - "Calls await process_message(msg) for each message" + - "Calls await consumer.commit() after processing" + - "consume_loop is started as a background task on startup" \ No newline at end of file diff --git a/data/coding/python/middle/rabbitmq.yaml b/data/coding/python/middle/rabbitmq.yaml new file mode 100644 index 0000000..3790449 --- /dev/null +++ b/data/coding/python/middle/rabbitmq.yaml @@ -0,0 +1,157 @@ +category: "RabbitMQ Client Development" +track: "python" +level: "middle" + +description: "Implement RabbitMQ consumers with manual acknowledgements and async consumption with aio-pika" + +tasks: + - id: "rabbitmq-m-001" + difficulty: 3 + tags: ["rabbitmq", "pika", "consumer", "manual-ack", "qos", "graceful-shutdown"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a RabbitMQ consumer with manual acknowledgements, + QoS prefetch count=1, and graceful shutdown on SIGINT. + + Your task: + Complete the `callback` function and the `consume` method so that: + - The callback processes the message body + - If processing succeeds, it sends basic_ack + - If processing fails, it sends basic_nack with requeue=True + - The consumer sets QoS prefetch_count=1 before consuming + - SIGINT triggers basic_cancel and connection close + + The connection setup and signal handler skeleton are provided. + ru: | + Контекст: + Нужно реализовать RabbitMQ консьюмера с ручными подтверждениями, + QoS prefetch count=1 и корректным завершением по SIGINT. + + Задача: + Реализуйте callback-функцию и метод `consume` так, чтобы: + - Callback обрабатывал тело сообщения + - При успехе отправлял basic_ack + - При ошибке отправлял basic_nack с requeue=True + - Consumer устанавливал QoS prefetch_count=1 + - SIGINT вызывал basic_cancel и закрытие соединения + starter_code: | + import pika + import signal + import sys + import logging + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + + class AckConsumer: + def __init__(self, host="localhost"): + self.connection = pika.BlockingConnection( + pika.ConnectionParameters(host) + ) + self.channel = self.connection.channel() + self.channel.basic_qos(prefetch_count=1) + + def callback(self, ch, method, properties, body): + """Process message, ack on success, nack on failure.""" + # TODO: log body, simulate processing, ack/nack accordingly + pass + + def consume(self, queue, consumer_tag="my_tag"): + """Start consuming with manual ack and signal handling.""" + # TODO: register signal handler for SIGINT, basic_consume with auto_ack=False, start_consuming + pass + + def close(self): + if self.connection and self.connection.is_open: + self.connection.close() + + + consumer = AckConsumer() + try: + consumer.consume("task_queue") + except KeyboardInterrupt: + consumer.close() + expected_points: + - "Callback calls ch.basic_ack() on success" + - "Callback calls ch.basic_nack(requeue=True) on failure" + - "QoS prefetch_count=1 is set before consuming" + - "SIGINT handler cancels consumer and closes connection" + - "auto_ack=False is set on basic_consume" + + - id: "rabbitmq-m-002" + difficulty: 3 + tags: ["rabbitmq", "aio-pika", "asyncio", "fastapi", "backpressure", "semaphore"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement an async RabbitMQ consumer using aio-pika + integrated with FastAPI, with concurrency limiting via Semaphore. + + Your task: + Complete the `process_message` coroutine and the `startup` event so that: + - The consumer connects to RabbitMQ using aio-pika with robust connection + - Sets QoS prefetch_count=20 + - Declares a durable queue and consumes messages + - Each message is processed inside a Semaphore context (max 10 concurrent) + - Uses `message.process(requeue=False)` context manager for auto ack/nack + + The FastAPI app skeleton is provided. + ru: | + Контекст: + Нужно реализовать асинхронного RabbitMQ консьюмера с aio-pika + в FastAPI приложении с ограничением конкурентности через Semaphore. + + Задача: + Реализуйте корутину `process_message` и событие `startup` так, чтобы: + - Consumer подключался к RabbitMQ через aio-pika connect_robust + - Устанавливал QoS prefetch_count=20 + - Объявлял durable очередь и запускал consume + - Каждое сообщение обрабатывалось в контексте Semaphore (макс. 10) + - Использовался `message.process(requeue=False)` для auto ack/nack + starter_code: | + from fastapi import FastAPI + import aio_pika + import asyncio + import logging + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + app = FastAPI() + connection = None + semaphore = asyncio.Semaphore(10) + + + async def process_message(message: aio_pika.IncomingMessage): + """Process message with concurrency limiting.""" + # TODO: use semaphore, use message.process(), log body, simulate work + pass + + + @app.on_event("startup") + async def startup(): + """Connect to RabbitMQ, declare queue, start consuming.""" + global connection + # TODO: connect_robust, create channel, set_qos, declare queue, consume + pass + + + @app.on_event("shutdown") + async def shutdown(): + global connection + if connection: + await connection.close() + expected_points: + - "Uses aio_pika.connect_robust() for connection" + - "Sets QoS prefetch_count=20 on the channel" + - "Declares a durable queue" + - "Uses async with semaphore: for concurrency limiting" + - "Uses async with message.process(requeue=False): for auto ack/nack" \ No newline at end of file diff --git a/data/coding/python/senior/kafka.yaml b/data/coding/python/senior/kafka.yaml new file mode 100644 index 0000000..b3529c6 --- /dev/null +++ b/data/coding/python/senior/kafka.yaml @@ -0,0 +1,244 @@ +category: "Kafka Client Production" +track: "python" +level: "senior" + +description: "Implement exactly-once semantics, Avro serialization with Schema Registry, and parallel processing with multiprocessing" + +tasks: + - id: "kafka-s-001" + difficulty: 4 + tags: ["kafka", "exactly-once", "idempotent", "transactions", "eos", "confluent-kafka"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a Kafka producer that guarantees exactly-once semantics + for producing messages to a topic. This involves configuring an idempotent + producer and using transactions. + + Your task: + Complete the `TransactionalProducer` class so that: + - The producer is configured with idempotence enabled + - It uses a unique transactional.id + - It initializes transactions + - The `send` method wraps the produce in a transaction (begin, produce, commit) + - It handles transaction failures by aborting + + Existing config parameters should be preserved. + ru: | + Контекст: + Нужно реализовать Kafka продюсера с exactly-once семантикой. + + Задача: + Реализуйте класс `TransactionalProducer` так, чтобы: + - Продюсер был настроен с идемпотентностью + - Использовал уникальный transactional.id + - Инициализировал транзакции + - Метод `send` оборачивал отправку в транзакцию (begin, produce, commit) + - Ошибки транзакции обрабатывались через abort + starter_code: | + from confluent_kafka import Producer + + + class TransactionalProducer: + def __init__(self, bootstrap_servers, transactional_id): + # TODO: configure with enable.idempotence=True, transactional.id, and acks='all' + self.producer = None + + def init(self): + """Initialize transactions.""" + # TODO: init_transactions() + pass + + def send(self, topic, key, value): + """Send a message within a transaction.""" + # TODO: begin_transaction, produce, commit_transaction (or abort on error) + pass + + def flush(self): + self.producer.flush() + + + # Usage: + # producer = TransactionalProducer('localhost:9092', 'producer-1') + # producer.init() + # producer.send('topic1', 'key1', b'value1') + # producer.flush() + expected_points: + - "Configures enable.idempotence=True and acks='all'" + - "Sets transactional.id in producer config" + - "Calls init_transactions() to initialize" + - "send() wraps produce in begin/commit_transaction" + - "On failure, calls abort_transaction() instead of commit" + + - id: "kafka-s-003" + difficulty: 4 + tags: ["kafka", "avro", "schema-registry", "serialization", "confluent-kafka"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement Avro serialization/deserialization with Confluent Schema Registry + for a Kafka producer and consumer. + + Your task: + Complete the producer and consumer setup so that: + - The producer serializes messages using AvroSerializer with a Schema Registry client + - The consumer deserializes messages using AvroDeserializer + - The schema defines a "User" record with "name" (string) and "age" (int) + - The SerializationContext is properly set with the topic and MessageField + + The Schema Registry URL, bootstrap servers, and the Avro schema are already defined. + ru: | + Контекст: + Нужно реализовать Avro сериализацию/десериализацию с Confluent Schema Registry. + + Задача: + Реализуйте продюсера и консьюмера так, чтобы: + - Продюсер сериализовал сообщения через AvroSerializer + - Консьюмер десериализовал через AvroDeserializer + - Схема определяла запись "User" с полями "name" (string) и "age" (int) + - SerializationContext был правильно настроен + starter_code: | + from confluent_kafka import Producer, Consumer + from confluent_kafka.serialization import SerializationContext, MessageField + from confluent_kafka.schema_registry import SchemaRegistryClient + from confluent_kafka.schema_registry.avro import AvroSerializer, AvroDeserializer + + schema_registry_conf = {"url": "http://localhost:8081"} + schema_registry_client = SchemaRegistryClient(schema_registry_conf) + + value_schema_str = """ + { + "type": "record", + "name": "User", + "fields": [ + {"name": "name", "type": "string"}, + {"name": "age", "type": "int"} + ] + } + """ + + # TODO: create AvroSerializer and AvroDeserializer + + # Producer config + producer_conf = {"bootstrap.servers": "localhost:9092"} + producer = Producer(producer_conf) + + user = {"name": "Alice", "age": 30} + # TODO: produce the message with Avro serializer + + # Consumer config + consumer_conf = {"bootstrap.servers": "localhost:9092", "group.id": "avro-group"} + consumer = Consumer(consumer_conf) + consumer.subscribe(["users"]) + + msg = consumer.poll(1.0) + if msg: + # TODO: deserialize the message with Avro deserializer + pass + + consumer.close() + expected_points: + - "Creates AvroSerializer with schema_registry_client and schema string" + - "Creates AvroDeserializer with schema_registry_client" + - "Producer calls produce with avro_serializer(user, SerializationContext('users', MessageField.VALUE))" + - "Consumer deserializes with avro_deserializer(msg.value(), SerializationContext('users', MessageField.VALUE))" + - "Schema Registry is used for schema storage and retrieval" + + - id: "kafka-s-004" + difficulty: 4 + tags: ["kafka", "multiprocessing", "high-throughput", "parallel-processing", "backpressure"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement parallel Kafka message processing using multiprocessing. + One consumer process reads messages and distributes them to worker processes + via a queue. + + Your task: + Complete the implementation so that: + - The consumer reads messages from Kafka and puts them into the multiprocessing queue + - Worker processes take messages from the queue and process them + - The queue is bounded (maxsize=1000) for backpressure + - Workers can be gracefully stopped by sending None sentinels + - The consumer commits offsets periodically (not after each message) + + The worker function skeleton and the main function are partially implemented. + ru: | + Контекст: + Нужно реализовать параллельную обработку Kafka сообщений через multiprocessing. + + Задача: + Реализуйте: один процесс-консьюмер читает из Kafka и распределяет сообщения + в воркер-процессы через очередь. + - Очередь ограничена (maxsize=1000) для backpressure + - Воркеры завершаются при получении None + - Consumer коммитит офсеты периодически + starter_code: | + import multiprocessing as mp + from confluent_kafka import Consumer + import time + import logging + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + + def worker_process(in_queue, worker_id): + """Take messages from queue and process them.""" + while True: + msg = in_queue.get() + if msg is None: + break + # TODO: simulate processing (time.sleep(0.1)), log result + pass + + + def main(): + queue = mp.Queue(maxsize=1000) + + # Start worker processes + num_workers = 4 + workers = [ + mp.Process(target=worker_process, args=(queue, i)) + for i in range(num_workers) + ] + for w in workers: + w.start() + + # Consumer process + consumer = Consumer({ + "bootstrap.servers": "localhost:9092", + "group.id": "mp-group", + "enable.auto.commit": False, + }) + consumer.subscribe(["topic"]) + + # TODO: poll loop - get messages, put in queue, commit periodically + try: + pass + finally: + # Stop workers + for _ in workers: + queue.put(None) + for w in workers: + w.join() + consumer.close() + + + if __name__ == "__main__": + main() + expected_points: + - "Worker processes loop reading from queue and processing" + - "Main consumer polls Kafka and puts messages into the queue" + - "Queue is bounded (maxsize=1000) for backpressure" + - "Workers are stopped via None sentinel" + - "Offsets are committed periodically (not per message)" \ No newline at end of file diff --git a/data/coding/python/senior/rabbitmq.yaml b/data/coding/python/senior/rabbitmq.yaml new file mode 100644 index 0000000..0dd95aa --- /dev/null +++ b/data/coding/python/senior/rabbitmq.yaml @@ -0,0 +1,217 @@ +category: "RabbitMQ Client Production" +track: "python" +level: "senior" + +description: "Implement RPC pattern over RabbitMQ and DLX-based retry with exponential backoff" + +tasks: + - id: "rabbitmq-s-001" + difficulty: 4 + tags: ["rabbitmq", "rpc", "pika", "correlation-id", "reply-to", "timeout"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a classic RPC pattern over RabbitMQ. + The client sends a request with a unique correlation_id and a reply_to queue. + The server processes the request and sends the response back. + + Your task: + Complete the RPC Client so that: + - The client declares an exclusive callback queue for responses + - Each request generates a unique correlation_id + - The request includes reply_to and correlation_id in properties + - The client waits for the response with a timeout + - If the timeout expires, it raises TimeoutError + + Complete the RPC Server so that: + - It consumes from the rpc_queue + - It processes the request (calls process_request) + - It sends the response to the reply_to queue with the same correlation_id + - It acknowledges the original message + + The server's process_request function is already implemented. + ru: | + Контекст: + Нужно реализовать RPC паттерн поверх RabbitMQ. + + Задача: + Реализуйте RPC клиент: + - Объявляет эксклюзивную очередь обратного вызова + - Каждый запрос получает уникальный correlation_id + - В свойствах сообщения передаются reply_to и correlation_id + - Клиент ждёт ответ с таймаутом + - При таймауте выбрасывает TimeoutError + + Реализуйте RPC сервер: + - Потребляет из rpc_queue + - Вызывает process_request + - Отправляет ответ в reply_to с тем же correlation_id + - Подтверждает исходное сообщение + starter_code: | + import pika + import uuid + import time + import logging + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + + def process_request(body): + """Process request and return response (simulated).""" + n = int(body) + return str(n * 2) + + + # --- RPC Client --- + class RpcClient: + def __init__(self): + self.connection = pika.BlockingConnection() + self.channel = self.connection.channel() + # TODO: declare exclusive callback queue + self.callback_queue = None + self.response = None + self.corr_id = None + + def on_response(self, ch, method, props, body): + """Match response by correlation_id.""" + # TODO: check if correlation_id matches, store response + pass + + def call(self, message, timeout=5): + """Send RPC request and wait for response.""" + # TODO: generate corr_id, publish with reply_to and correlation_id, + # wait for response with timeout, return response + pass + + + # --- RPC Server --- + class RpcServer: + def __init__(self): + self.connection = pika.BlockingConnection() + self.channel = self.connection.channel() + # TODO: declare rpc_queue, set QoS, consume + pass + + def on_request(self, ch, method, props, body): + """Handle incoming RPC request.""" + # TODO: process request, send response to reply_to, ack + pass + + def start(self): + self.channel.start_consuming() + + + # Usage: + # client = RpcClient() + # response = client.call("5") + # print(f"Response: {response}") + expected_points: + - "Client declares exclusive callback queue" + - "Each request has unique correlation_id" + - "Request properties include reply_to and correlation_id" + - "Client waits with timeout using process_data_events" + - "Server processes request and sends response to reply_to" + - "Server acks the original message after processing" + - "Server uses prefetch_count=1" + + - id: "rabbitmq-s-002" + difficulty: 4 + tags: ["rabbitmq", "dlx", "retry", "exponential-backoff", "ttl", "dead-letter"] + coding: + language: python + evaluation_mode: ai + assignment: + en: | + Context: + You need to implement a retry pattern with dead-letter exchange (DLX) in RabbitMQ. + When a message processing fails, it should be sent to a retry queue with a TTL delay, + and then automatically returned to the main queue for reprocessing. + + Your task: + Complete the setup so that: + - A main queue is declared with DLX pointing to retry_exchange + - A retry queue is declared with TTL (5000ms) and DLX pointing back to the main exchange + - The retry exchange is a direct exchange + - The consumer acks on success, nacks with requeue=False on failure (message goes to DLX → retry) + - After TTL expires, the message automatically returns to the main queue + + The exchanges and the consumer callback are partially implemented. + ru: | + Контекст: + Нужно реализовать паттерн повтора с Dead Letter Exchange (DLX) в RabbitMQ. + При ошибке обработки сообщение отправляется в очередь повтора с TTL задержкой, + после чего автоматически возвращается в основную очередь. + + Задача: + Настройте инфраструктуру так, чтобы: + - Основная очередь объявлена с DLX, указывающим на retry_exchange + - Очередь повтора имеет TTL (5000ms) и DLX обратно на основной exchange + - Consumer подтверждает успех, при ошибке nack с requeue=False + - По истечении TTL сообщение автоматически возвращается в основную очередь + starter_code: | + import pika + import logging + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + + class RetryConsumer: + def __init__(self, host="localhost"): + self.connection = pika.BlockingConnection( + pika.ConnectionParameters(host) + ) + self.channel = self.connection.channel() + + # Declare exchanges + self.channel.exchange_declare( + "dlx_exchange", exchange_type="direct", durable=True + ) + self.channel.exchange_declare( + "retry_exchange", exchange_type="direct", durable=True + ) + + # TODO: declare main_queue with DLX -> retry_exchange + # TODO: declare retry_queue with TTL and DLX -> dlx_exchange + # TODO: bind queues to exchanges + + self.channel.basic_qos(prefetch_count=1) + + def callback(self, ch, method, properties, body): + """Process message, ack on success, nack on failure.""" + try: + logger.info(f"Processing: {body}") + # simulate processing + ch.basic_ack(delivery_tag=method.delivery_tag) + except Exception: + # TODO: nack with requeue=False -> message goes to DLX -> retry queue + pass + + def consume(self, queue): + self.channel.basic_consume( + queue, self.callback, auto_ack=False + ) + logger.info("Waiting for messages...") + self.channel.start_consuming() + + def close(self): + if self.connection and self.connection.is_open: + self.connection.close() + + + consumer = RetryConsumer() + try: + consumer.consume("main_queue") + except KeyboardInterrupt: + consumer.close() + expected_points: + - "Declares dlx_exchange and retry_exchange as direct" + - "Main queue has x-dead-letter-exchange=retry_exchange and x-dead-letter-routing-key=retry" + - "Retry queue has x-dead-letter-exchange=dlx_exchange, x-dead-letter-routing-key=main, and x-message-ttl=5000" + - "Queues are bound to their respective exchanges" + - "Consumer nacks with requeue=False on failure → message goes to DLX → retry queue" + - "After TTL, message returns to main queue automatically" \ No newline at end of file diff --git a/data/config.json.bak b/data/config.json.bak new file mode 100644 index 0000000..59218d1 --- /dev/null +++ b/data/config.json.bak @@ -0,0 +1,7 @@ +{ + "timeout": 60.0, + "locale": "en", + "speech_model_size": "medium", + "question_voice_enabled": false, + "tts_voice_id": "en_US-lessac-medium" +} \ No newline at end of file diff --git a/data/config.json.tmp b/data/config.json.tmp new file mode 100644 index 0000000..e183951 --- /dev/null +++ b/data/config.json.tmp @@ -0,0 +1,7 @@ +{ + "timeout": 120.0, + "locale": "ru", + "speech_model_size": "medium", + "question_voice_enabled": false, + "tts_voice_id": "ru_RU-irina-medium" +} diff --git a/data/llm_models.json.bak b/data/llm_models.json.bak new file mode 100644 index 0000000..b28e248 --- /dev/null +++ b/data/llm_models.json.bak @@ -0,0 +1,14 @@ +{ + "selected": "google-gemini-3-1-flash-lite-polza", + "models": { + "google-gemini-3-1-flash-lite-polza": { + "display_name": "google/gemini-3.1-flash-lite(polza)", + "provider_type": "openai-compatible", + "model": "google/gemini-3.1-flash-lite", + "base_url": "https://polza.ai/api/v1", + "api_key_required": true, + "accepts_audio_input": true, + "api_key": "pza_SwTJ0dA49_faG9bcz5TPi6i2EFg74GWw" + } + } +} \ No newline at end of file diff --git a/data/questions/airflow/junior/configuration.yaml b/data/questions/airflow/junior/configuration.yaml index 7da4b08..6cffbae 100644 --- a/data/questions/airflow/junior/configuration.yaml +++ b/data/questions/airflow/junior/configuration.yaml @@ -17,43 +17,7 @@ questions: How to avoid hardcoding passwords? What is a secrets backend (e.g., HashiCorp Vault, AWS Secrets Manager)? ru: Как управлять конфигурацией и секретами в Airflow? Объясните Variables (через UI или CLI) и Connections. Как избежать жёсткого кодирования паролей? Что такое бэкенд секретов (например, HashiCorp Vault, AWS Secrets Manager)? - code: '# Using Variables in DAG - - from airflow.models import Variable - - - api_url = Variable.get("API_URL") - - # Optional default - - api_key = Variable.get("API_KEY", default_var="default-key") - - # JSON variable - - config = Variable.get("CONFIG", deserialize_json=True) - - - # Using Connection in Hook - - from airflow.providers.postgres.hooks.postgres import PostgresHook - - hook = PostgresHook(postgres_conn_id=''my_db'') - - - # Define connection via environment variable (AIRFLOW_CONN_MY_DB) - - # Format: postgresql://user:pass@host:5432/db - - - # Secrets backend config in airflow.cfg - - [secrets] - - backend = airflow.providers.hashicorp.secrets.vault.VaultBackend - - backend_kwargs = {"connections_path": "airflow/connections", "variables_path": "airflow/variables"} - - ' + code: null follow_ups: en: - What is the difference between Variable.get and using Jinja templates with {{ var.value.xyz }}? @@ -84,12 +48,7 @@ questions: ru: Как передавать данные между задачами в Airflow? Объясните XComs, их хранение (в БД метаданных) и ограничения (размер, сериализация). Сравните классические XComs (xcom_push/xcom_pull) с автоматической передачей через TaskFlow API (возвращаемые значения и декоратор @task). - code: "# Classic XCom (pre-2.0 style)\ndef push_function(**context):\n context['ti'].xcom_push(key='data', value={'foo':\ - \ 'bar'})\n\nPythonOperator(task_id='push', python_callable=push_function)\n\ndef pull_function(**context):\n data\ - \ = context['ti'].xcom_pull(task_ids='push', key='data')\n print(data)\n\n# TaskFlow API (2.0+)\n@task\ndef extract():\n\ - \ return {'user': 'alice', 'score': 100}\n\n@task\ndef process(data):\n print(f\"Processing {data['user']}\")\n\ - \ return data['score'] * 2\n\n@task\ndef save(result):\n print(f\"Saved: {result}\")\n\n# Dependencies inferred\ - \ automatically\nsave(process(extract()))\n" + code: null follow_ups: en: - What is the size limit for XCom values? How to change it? diff --git a/data/questions/airflow/junior/fundamentals.yaml b/data/questions/airflow/junior/fundamentals.yaml index 380b1c9..588e108 100644 --- a/data/questions/airflow/junior/fundamentals.yaml +++ b/data/questions/airflow/junior/fundamentals.yaml @@ -17,9 +17,7 @@ questions: Database? How do they interact? What is a DAG and what is its purpose?' ru: 'Объясните архитектуру Apache Airflow. Каковы основные компоненты: Scheduler, Webserver, Executor, Worker, Metadata Database? Как они взаимодействуют? Что такое DAG и какова его цель?' - code: "# A simple DAG definition\nfrom airflow import DAG\nfrom airflow.operators.dummy import DummyOperator\nfrom datetime\ - \ import datetime\n\nwith DAG('example_dag', start_date=datetime(2024,1,1), schedule_interval='@daily') as dag:\n \ - \ start = DummyOperator(task_id='start')\n end = DummyOperator(task_id='end')\n start >> end\n" + code: null follow_ups: en: - What is the role of the Scheduler? How does it parse DAGs? @@ -48,11 +46,7 @@ questions: etc.). How do Sensors differ from Operators? What is the purpose of Hooks? ru: Что такое Operators, Sensors и Hooks в Airflow? Назовите распространённые операторы (BashOperator, PythonOperator, SQLExecuteQueryOperator и др.). Чем Sensors отличаются от Operators? Какова цель Hooks? - code: "from airflow.operators.bash import BashOperator\nfrom airflow.operators.python import PythonOperator\nfrom airflow.sensors.filesystem\ - \ import FileSensor\nfrom airflow.providers.postgres.hooks.postgres import PostgresHook\n\ntask_bash = BashOperator(task_id='date',\ - \ bash_command='date')\n\ndef my_func():\n hook = PostgresHook(postgres_conn_id='pg_default')\n data = hook.get_records(sql=\"\ - SELECT * FROM table\")\n print(data)\n\ntask_python = PythonOperator(task_id='process', python_callable=my_func)\n\ - wait_for_file = FileSensor(task_id='wait_for_file', filepath='/tmp/data.csv', poke_interval=30)\n" + code: null follow_ups: en: - How to create a custom operator? @@ -80,12 +74,7 @@ questions: chaining. What is the difference between cross-DAG dependencies (ExternalTaskSensor) and trigger DAGs (TriggerDagRunOperator)? ru: Как определить зависимости между задачами в Airflow? Объясните операторы bitshift (>>, <<), set_upstream/set_downstream и цепочки. В чём разница между зависимостями между DAG (ExternalTaskSensor) и запуском одного DAG из другого (TriggerDagRunOperator)? - code: "with DAG(...) as dag:\n t1 = DummyOperator(task_id='t1')\n t2 = DummyOperator(task_id='t2')\n t3 = DummyOperator(task_id='t3')\n\ - \ t4 = DummyOperator(task_id='t4')\n\n# Bitshift syntax\nt1 >> [t2, t3] >> t4\n# Equivalent: t1.set_downstream(t2);\ - \ t2.set_upstream(t1)\n\n# Cross-DAG dependencies: wait for another DAG's task\nExternalTaskSensor(\n task_id='wait_for_dag1',\n\ - \ external_dag_id='dag1',\n external_task_id='final_task',\n allowed_states=['success']\n)\n\n# Trigger a DAG\ - \ from another DAG\nTriggerDagRunOperator(\n task_id='trigger_dag2',\n trigger_dag_id='dag2',\n conf={'key':\ - \ 'value'}\n)\n" + code: null follow_ups: en: - What are dynamic task mappings (map, expand) in Airflow 2.3+? diff --git a/data/questions/airflow/middle/executors.yaml b/data/questions/airflow/middle/executors.yaml index 4374fef..b51c140 100644 --- a/data/questions/airflow/middle/executors.yaml +++ b/data/questions/airflow/middle/executors.yaml @@ -19,11 +19,7 @@ questions: ru: 'Сравните исполнители Airflow: SequentialExecutor, LocalExecutor, CeleryExecutor, KubernetesExecutor. Как CeleryExecutor распределяет задачи? Каковы плюсы/минусы KubernetesExecutor по сравнению с CeleryExecutor? Когда использовать LocalExecutor в продакшене?' - code: "# airflow.cfg\nexecutor = CeleryExecutor\ncelery_app_name = airflow.executors.celery_executor\n\n# For KubernetesExecutor:\ - \ optional pod template\nexecutor = KubernetesExecutor\npod_template_file = /opt/airflow/pod_template.yaml\n\n# Resource\ - \ limits per task (KubernetesExecutor or CeleryKubernetes)\nwith DAG(...) as dag:\n task = PythonOperator(\n \ - \ task_id='heavy_task',\n python_callable=run_model,\n executor_config={\"KubernetesExecutor\": {\"\ - requests\": {\"memory\": \"8Gi\"}}}\n )\n" + code: null follow_ups: en: - How to set up Celery with Redis or RabbitMQ as broker? diff --git a/data/questions/airflow/middle/operations.yaml b/data/questions/airflow/middle/operations.yaml index 9552240..1688959 100644 --- a/data/questions/airflow/middle/operations.yaml +++ b/data/questions/airflow/middle/operations.yaml @@ -18,14 +18,7 @@ questions: ru: 'Как тестировать DAG в Airflow? Объясните: валидация DAG (airflow dags test), unit-тестирование с pytest, мокирование операторов/хуков, использование DagBag для тестов загрузки. Как протестировать зависимости задач без запуска внешних систем?' - code: "# test_dag.py\nfrom airflow.models import DagBag\n\ndef test_dag_loaded():\n dag_bag = DagBag(dag_folder='/path/to/dags')\n\ - \ assert dag_bag.dags['my_dag'] is not None\n assert not dag_bag.import_errors\n\ndef test_dag_structure():\n\ - \ dag = dag_bag.get_dag('my_dag')\n assert dag.task_dict.keys() == {'t1', 't2', 't3'}\n assert dag.task_dict['t1'].downstream_task_ids\ - \ == {'t2'}\n\n# Using pytest with Airflow's test utilities\nfrom airflow.utils.dag_cycle_tester import check_cycle\n\ - \ndef test_no_cycle():\n dag = create_dag()\n check_cycle(dag) # raises if cycle\n\n# Test task with mocked hook\n\ - from unittest.mock import patch\ndef test_task():\n with patch('airflow.providers.postgres.hooks.postgres.PostgresHook.run')\ - \ as mock_run:\n task = PythonOperator(task_id='test', python_callable=lambda: None)\n task.execute(context={'ds':\ - \ '2024-01-01'})\n mock_run.assert_called_once()\n" + code: null follow_ups: en: - How to test sensor logic (timeout, poke interval)? @@ -57,12 +50,7 @@ questions: ru: Как мониторить состояние и производительность Airflow? Объясните метрики, передаваемые через StatsD (с приёмником Prometheus), отслеживание длительности DAG, сбоев задач, отставания планировщика. Как настроить оповещения о зависших DAG, heartbeat планировщика и длине очереди Celery? Какова цель health endpoint в Airflow? - code: "# airflow.cfg for metrics\n[metrics]\nstatsd_on = True\nstatsd_host = localhost\nstatsd_port = 8125\nstatsd_prefix\ - \ = airflow\n\n# Expose Prometheus endpoint (using statsd-exporter or native Prometheus)\n[metrics]\nmetrics_backend\ - \ = statsd\nstatsd_host = prometheus-statsd-exporter:9125\n\n# Health endpoint\n# GET /health\n{\n \"metadatabase\"\ - : {\"status\": \"healthy\"},\n \"scheduler\": {\"status\": \"healthy\", \"latest_scheduler_heartbeat\": \"2024-01-01T00:00:00Z\"\ - }\n}\n\n# Key metrics to monitor\n# dag_processing.processes (scheduler health)\n# scheduler.tasks.running, .queued,\ - \ .starving\n# ti.finish.{dag_id}.{task_id}.{status}\n# celery.worker.consumed, .queue_length (if CeleryExecutor)\n" + code: null follow_ups: en: - How to detect a DAG that hasn't run for X hours? @@ -95,42 +83,7 @@ questions: ru: 'Как защитить развёртывание Airflow? Объясните встроенный RBAC (управление доступом на основе ролей): роли (Admin, User, Op, Viewer), разрешения на DAG и ресурсы. Как интегрировать с LDAP, OAuth (Google, GitHub) или OpenID Connect? Для чего используется `fernet_key`?' - code: '# airflow.cfg authentication - - [webserver] - - authenticate = True - - auth_backend = airflow.api.auth.backend.basic_auth - - # or - - auth_backend = airflow.providers.fab.auth_backend.oauth - - - # OAuth configuration (e.g., GitHub) - - [oauth] - - enabled = True - - oauth_provider = github - - client_id = xxx - - client_secret = xxx - - - # RBAC: custom role definition via CLI or UI - - airflow roles add-permission --role MyRole --resource DAG:example_dag --can_edit - - - # Fernet key for encrypting connection passwords in DB - - fernet_key = cXVhbnR1bXNreWlzZWN1cmVhbmRvbmdzdHJpbmcxMjM= - - ' + code: null follow_ups: en: - How to restrict access to specific DAGs per team? diff --git a/data/questions/airflow/middle/scheduling.yaml b/data/questions/airflow/middle/scheduling.yaml index 4b8035e..fc47f92 100644 --- a/data/questions/airflow/middle/scheduling.yaml +++ b/data/questions/airflow/middle/scheduling.yaml @@ -20,10 +20,7 @@ questions: ru: Как работает планирование в Airflow? Объясните логическую дату (execution_date), start_date, schedule_interval, catchup и обратное заполнение (backfill). В чём разница между depends_on_past и wait_for_downstream? Как выполнить backfill исторических данных без запуска нижестоящих DAG? - code: "# DAG with catchup disabled\nwith DAG('daily_etl',\n start_date=datetime(2024,1,1),\n schedule_interval='@daily',\n\ - \ catchup=False) as dag:\n ...\n\n# Backfill via CLI\nairflow dags backfill daily_etl --start-date 2024-01-01\ - \ --end-date 2024-01-10\n\n# depends_on_past: task run waits for previous task run (same DAG, same task, previous execution\ - \ date)\nPythonOperator(task_id='validate', depends_on_past=True, wait_for_downstream=False, ...)\n\n# wait_for_downstream:\ + code: null \ waits for all previous task runs within the same execution date's downstream tasks\n" follow_ups: en: @@ -56,10 +53,7 @@ questions: ru: 'Каковы лучшие практики написания продакшен- DAG в Airflow? Объясните: избегание кода верхнего уровня, обращающегося к БД, идемпотентность, использование идемпотентных операторов, динамическая генерация DAG (через Jinja или Python). Почему задачи должны быть идемпотентными?' - code: "# BAD: top-level code loading data\nfrom my_db import get_config\nCONFIG = get_config() # Executed on every scheduler\ - \ heartbeat\n\n# GOOD: lazy loading inside tasks\n@task\ndef get_config_task():\n from my_db import get_config\n\ - \ return get_config()\n\n# Dynamic DAG generation (avoid too many files)\ndef generate_dag(dag_id, schedule):\n \ - \ with DAG(dag_id, schedule_interval=schedule) as dag:\n task = BashOperator(task_id='run', bash_command=f'echo\ + code: null \ {dag_id}')\n return dag\n\nfor customer in ['cust_a', 'cust_b']:\n globals()[f'dag_{customer}'] = generate_dag(f'process_{customer}',\ \ '@daily')\n" follow_ups: diff --git a/data/questions/airflow/middle/taskflow.yaml b/data/questions/airflow/middle/taskflow.yaml index 5ad7aba..ac3310d 100644 --- a/data/questions/airflow/middle/taskflow.yaml +++ b/data/questions/airflow/middle/taskflow.yaml @@ -19,14 +19,7 @@ questions: ru: Что такое TaskFlow API в Airflow 2.0? Как он упрощает написание DAG? Объясните декораторы @dag и @task, автоматическую передачу XCom и использование нескольких выходов (MultipleOutputs, expand). Когда следует использовать TaskFlow вместо традиционных операторов? - code: "from airflow.decorators import dag, task\nfrom datetime import datetime\n\n@dag(schedule_interval='@daily', start_date=datetime(2024,1,1),\ - \ catchup=False)\ndef my_pipeline():\n \n @task\n def extract():\n return {'ids': [1,2,3], 'names':\ - \ ['alice','bob']}\n \n @task\n def process(data):\n return [f\"processed_{n}\" for n in data['names']]\n\ - \ \n @task\n def save(results):\n print(f\"Saving {results}\")\n return len(results)\n \n\ - \ # Automatic dependency inference\n count = save(process(extract()))\n \n # Multiple outputs\n @task(multiple_outputs=True)\n\ - \ def split():\n return {'odd': 1, 'even': 2}\n \n # Dynamic task mapping (2.3+)\n @task\n def\ - \ process_one(x):\n return x * 2\n \n numbers = [1,2,3]\n process_one.expand(x=numbers) # creates 3\ - \ mapped tasks\n\ndag = my_pipeline()\n" + code: null follow_ups: en: - Can I mix traditional operators with TaskFlow tasks? diff --git a/data/questions/airflow/senior/production.yaml b/data/questions/airflow/senior/production.yaml index 5eb3a72..3217512 100644 --- a/data/questions/airflow/senior/production.yaml +++ b/data/questions/airflow/senior/production.yaml @@ -20,14 +20,7 @@ questions: ru: Что такое отложенные операторы (deferrable operators) в Airflow 2.2+? Как они снижают потребление ресурсов воркеров? Объясните концепцию Trigger, компонент Triggerer и разницу между классическим сенсором с poke_interval и отложенным режимом. Покажите пример отложенного оператора. - code: "# Classic sensor (worker sits idle poking)\nwait = FileSensor(\n task_id='wait_for_file',\n filepath='/data/file.csv',\n\ - \ poke_interval=30,\n timeout=3600,\n mode='poke' # default, occupies worker slot\n)\n\n# Deferrable sensor\ - \ (frees worker)\nwait = FileSensor(\n task_id='wait_for_file',\n filepath='/data/file.csv',\n timeout=3600,\n\ - \ mode='reschedule' # older, still consumes DB writes\n)\n\n# True deferrable operator (Airflow 2.2+ with Triggerer)\n\ - from airflow.providers.amazon.aws.sensors.s3 import S3KeySensor\nwait = S3KeySensor(\n task_id='wait_for_s3',\n \ - \ bucket_key='data.csv',\n deferrable=True # uses Triggerer\n)\n\n# Custom deferrable operator (simplified)\n\ - class MyDeferrableSensor(BaseSensorOperator):\n def execute(self, context):\n self.defer(trigger=MyTrigger(),\ - \ method_name=\"execute_complete\")\n" + code: null follow_ups: en: - What is the role of the Triggerer process? @@ -59,12 +52,7 @@ questions: ru: 'Как развернуть Airflow в Kubernetes? Объясните Helm чарт (community или официальный). Опишите поды: scheduler, webserver, workers (Celery) и опциональные компоненты (Redis, Postgres, Flower). Когда использовать KubernetesExecutor вместо CeleryExecutor на K8s? Как управлять DAG с помощью GitSync или persistent volume?' - code: "# values.yaml for official Helm chart\nexecutor: CeleryExecutor\n# or executor: KubernetesExecutor\n\n# DAGs persistence\n\ - dags:\n persistence:\n enabled: true\n size: 10Gi\n gitSync:\n enabled: true\n repo: https://github.com/org/airflow-dags.git\n\ - \ branch: main\n syncWait: 60\n\n# Airflow configuration override\nconfig:\n core:\n load_examples: False\n\ - \ scheduler:\n min_file_process_interval: 30\n\n# KubernetesExecutor pod template\nworkers:\n kubernetes:\n \ - \ pod_template_file: /opt/airflow/pod_templates/pod_template.yaml\n\n# Install\nhelm repo add apache-airflow https://airflow.apache.org\n\ - helm install airflow apache-airflow/airflow -f values.yaml\n" + code: null follow_ups: en: - How to scale scheduler replicas (highly available) in Airflow 2.6+? @@ -96,14 +84,7 @@ questions: ru: 'Какие существуют паттерны динамической генерации DAG в Airflow? Сравните статические DAG (много файлов) с динамической генерацией из конфига (JSON/YAML). Объясните риски: производительность планировщика и время парсинга DAG. Как обрабатывать большое количество похожих DAG (например, на арендатора) без перегрузки планировщика?' - code: "# Dynamic DAGs using Python (single file)\ndef create_dag(tenant_name, schedule):\n with DAG(dag_id=f'{tenant_name}_daily_etl',\ - \ schedule_interval=schedule) as dag:\n start = DummyOperator(task_id='start')\n process = BashOperator(task_id='process',\ - \ bash_command=f'run_for_tenant {tenant_name}')\n start >> process\n return dag\n\ntenants = [{'name': 'acme',\ - \ 'schedule': '@daily'}, {'name': 'globex', 'schedule': '@hourly'}]\nfor t in tenants:\n globals()[f'dag_{t[\"name\"\ - ]}'] = create_dag(t['name'], t['schedule'])\n\n# Better: use Airflow 2.3+ Dynamic DAGs with DAG definition and config\n\ - # But still caution with many DAGs\n\n# Alternative: single parametrized DAG using Airflow's config\nwith DAG(dag_id='tenant_etl',\ - \ params={'tenant': 'default'}) as dag:\n task = BashOperator(task_id='run', bash_command='run_tenant {{ params.tenant\ - \ }}')\n# Trigger with API: POST /dags/tenant_etl/dagRuns with JSON {\"conf\": {\"tenant\": \"acme\"}}\n" + code: null follow_ups: en: - How many DAGs can a scheduler handle (ballpark)? diff --git a/data/questions/database/junior/design-basics.yaml b/data/questions/database/junior/design-basics.yaml index a26e8bb..6a50a6e 100644 --- a/data/questions/database/junior/design-basics.yaml +++ b/data/questions/database/junior/design-basics.yaml @@ -37,13 +37,7 @@ questions: text: en: "What are primary keys, foreign keys, and constraints in SQL databases?" ru: "Что такое первичные и внешние ключи и ограничения (constraints) в SQL-базах?" - code: | - CREATE TABLE orders ( - id SERIAL PRIMARY KEY, - user_id INTEGER NOT NULL REFERENCES users(id), - amount DECIMAL(10,2) CHECK (amount > 0), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ); + code: null follow_ups: en: - "What is a composite primary key?" diff --git a/data/questions/database/junior/mysql-basics.yaml b/data/questions/database/junior/mysql-basics.yaml index 1fff813..3abe499 100644 --- a/data/questions/database/junior/mysql-basics.yaml +++ b/data/questions/database/junior/mysql-basics.yaml @@ -37,11 +37,7 @@ questions: text: en: "What MySQL-specific administrative commands exist (SHOW, DESCRIBE, EXPLAIN)?" ru: "Какие административные команды специфичны для MySQL (SHOW, DESCRIBE, EXPLAIN)?" - code: | - SHOW DATABASES; - SHOW TABLES; - DESCRIBE users; - SHOW CREATE TABLE users; + code: null follow_ups: en: - "How does EXPLAIN help understand query execution?" @@ -65,13 +61,7 @@ questions: text: en: "How does AUTO_INCREMENT work in MySQL? How do you get the last inserted ID?" ru: "Как работает AUTO_INCREMENT в MySQL? Как получить ID последней вставленной строки?" - code: | - CREATE TABLE users ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(100) - ); - INSERT INTO users (name) VALUES ('Alice'); - SELECT LAST_INSERT_ID(); + code: null follow_ups: en: - "What happens to auto-increment values on rollback?" @@ -119,11 +109,7 @@ questions: text: en: "How do you create and manage databases and users in MySQL?" ru: "Как создавать и управлять базами данных и пользователями в MySQL?" - code: | - CREATE DATABASE mydb CHARACTER SET utf8mb4; - CREATE USER 'app'@'localhost' IDENTIFIED BY 'password'; - GRANT SELECT, INSERT, UPDATE ON mydb.* TO 'app'@'localhost'; - FLUSH PRIVILEGES; + code: null follow_ups: en: - "What is the difference between GRANT ALL and specific privileges?" diff --git a/data/questions/database/junior/redis-basics.yaml b/data/questions/database/junior/redis-basics.yaml index 6d18d12..3fef317 100644 --- a/data/questions/database/junior/redis-basics.yaml +++ b/data/questions/database/junior/redis-basics.yaml @@ -34,9 +34,7 @@ questions: text: en: "What Redis data structures would you use for a user session vs a user profile hash?" ru: "Какие структуры Redis использовать для user session vs hash профиля?" - code: | - SET session:abc123 "{user_id: 1}" EX 3600 - HSET user:1 name "Alice" email "a@example.com" + code: null follow_ups: en: - "When use JSON string vs Redis Hash?" @@ -57,10 +55,7 @@ questions: text: en: "What is TTL in Redis? How do EXPIRE and SET with EX option work?" ru: "Что такое TTL в Redis? Как работают EXPIRE и SET с опцией EX?" - code: | - SET cache:article:42 "payload" EX 300 - EXPIRE cache:article:42 300 - TTL cache:article:42 + code: null follow_ups: en: - "What TTL value indicates a key has no expiry?" @@ -103,9 +98,7 @@ questions: text: en: "What are Redis Lists and Sets used for? Give a practical example of each." ru: "Для чего Redis Lists и Sets? Приведите практический пример каждого." - code: | - LPUSH queue:emails "msg1" - SADD tags:article:1 "python" "redis" + code: null follow_ups: en: - "How do Sets differ from Lists?" @@ -168,10 +161,7 @@ questions: text: en: "How do you connect to Redis from Python? What is the difference between redis-py sync and redis.asyncio?" ru: "Как подключиться к Redis из Python? Чем отличается redis-py sync от redis.asyncio?" - code: | - import redis - r = redis.Redis(host="localhost", port=6379, decode_responses=True) - r.set("key", "value") + code: null follow_ups: en: - "Why use connection pooling?" diff --git a/data/questions/database/junior/sql-basics.yaml b/data/questions/database/junior/sql-basics.yaml index 06769eb..207c26d 100644 --- a/data/questions/database/junior/sql-basics.yaml +++ b/data/questions/database/junior/sql-basics.yaml @@ -13,10 +13,7 @@ questions: text: en: "Explain the basic structure of a SELECT query. How does WHERE clause filter data?" ru: "Объясните базовую структуру запроса SELECT. Как предложение WHERE фильтрует данные?" - code: | - SELECT column1, column2 - FROM table_name - WHERE condition; + code: null follow_ups: en: - "What is the order of execution of SQL clauses?" @@ -40,10 +37,7 @@ questions: text: en: "How do INSERT, UPDATE, and DELETE statements work? What are the risks of each?" ru: "Как работают операторы INSERT, UPDATE и DELETE? Какие риски у каждого?" - code: | - INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'); - UPDATE users SET email = 'new@example.com' WHERE id = 1; - DELETE FROM users WHERE id = 1; + code: null follow_ups: en: - "What happens if you omit the WHERE clause on UPDATE or DELETE?" @@ -67,10 +61,7 @@ questions: text: en: "What are the different types of JOINs in SQL? Explain INNER, LEFT, RIGHT, and FULL OUTER JOIN." ru: "Какие типы JOIN существуют в SQL? Объясните INNER, LEFT, RIGHT и FULL OUTER JOIN." - code: | - SELECT u.name, o.amount - FROM users u - INNER JOIN orders o ON u.id = o.user_id; + code: null follow_ups: en: - "What rows does each JOIN type return?" @@ -94,12 +85,7 @@ questions: text: en: "How does GROUP BY work with aggregate functions? What is the difference between WHERE and HAVING?" ru: "Как GROUP BY работает с агрегатными функциями? В чём разница между WHERE и HAVING?" - code: | - SELECT department, COUNT(*) as emp_count, AVG(salary) as avg_salary - FROM employees - WHERE status = 'active' - GROUP BY department - HAVING COUNT(*) > 5; + code: null follow_ups: en: - "What aggregate functions does SQL provide (COUNT, SUM, AVG, MIN, MAX)?" @@ -123,11 +109,7 @@ questions: text: en: "How do ORDER BY, LIMIT, and DISTINCT work in SQL queries?" ru: "Как работают ORDER BY, LIMIT и DISTINCT в SQL-запросах?" - code: | - SELECT DISTINCT department - FROM employees - ORDER BY department DESC - LIMIT 10; + code: null follow_ups: en: - "What is the default sort order in ORDER BY?" diff --git a/data/questions/database/junior/sqlite-basics.yaml b/data/questions/database/junior/sqlite-basics.yaml index a212de0..aad22ce 100644 --- a/data/questions/database/junior/sqlite-basics.yaml +++ b/data/questions/database/junior/sqlite-basics.yaml @@ -34,8 +34,7 @@ questions: text: en: "What is WAL (Write-Ahead Logging) mode in SQLite? What benefits does it provide?" ru: "Что такое WAL (Write-Ahead Logging) в SQLite? Какие преимущества даёт?" - code: | - PRAGMA journal_mode=WAL; + code: null follow_ups: en: - "How does WAL improve read concurrency?" @@ -98,10 +97,7 @@ questions: text: en: "How do you use SQLite from Python? Compare sqlite3 stdlib and aiosqlite." ru: "Как использовать SQLite из Python? Сравните sqlite3 stdlib и aiosqlite." - code: | - import sqlite3 - conn = sqlite3.connect("app.db") - conn.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)") + code: null follow_ups: en: - "What is row_factory useful for?" @@ -143,8 +139,7 @@ questions: text: en: "How do you backup a SQLite database safely while the app is running?" ru: "Как безопасно сделать backup SQLite при работающем приложении?" - code: | - .backup main backup.db + code: null follow_ups: en: - "Why is copying the .db file directly risky during writes?" diff --git a/data/questions/database/middle/locking-concurrency.yaml b/data/questions/database/middle/locking-concurrency.yaml index ea1a505..0fff392 100644 --- a/data/questions/database/middle/locking-concurrency.yaml +++ b/data/questions/database/middle/locking-concurrency.yaml @@ -34,11 +34,7 @@ questions: text: en: "What does SELECT FOR UPDATE do? When would you use it?" ru: "Что делает SELECT FOR UPDATE? Когда его использовать?" - code: | - BEGIN; - SELECT balance FROM accounts WHERE id = 1 FOR UPDATE; - UPDATE accounts SET balance = balance - 100 WHERE id = 1; - COMMIT; + code: null follow_ups: en: - "What is SELECT FOR UPDATE SKIP LOCKED?" @@ -80,10 +76,7 @@ questions: text: en: "Compare optimistic and pessimistic locking. How is optimistic locking implemented with a version column?" ru: "Сравните optimistic и pessimistic locking. Как optimistic locking реализуют через version column?" - code: | - UPDATE products - SET stock = stock - 1, version = version + 1 - WHERE id = 42 AND version = 5; + code: null follow_ups: en: - "When prefer optimistic over pessimistic?" diff --git a/data/questions/database/middle/migrations.yaml b/data/questions/database/middle/migrations.yaml index a8c5a85..c15fb03 100644 --- a/data/questions/database/middle/migrations.yaml +++ b/data/questions/database/middle/migrations.yaml @@ -13,9 +13,7 @@ questions: text: en: "What is Alembic and how does it relate to SQLAlchemy?" ru: "Что такое Alembic и как он связан с SQLAlchemy?" - code: | - alembic revision --autogenerate -m "add users table" - alembic upgrade head + code: null follow_ups: en: - "What is stored in alembic_version?" @@ -141,8 +139,7 @@ questions: text: en: "How do you add an index to a large PostgreSQL table with minimal locking?" ru: "Как добавить index на большую таблицу PostgreSQL с минимальной блокировкой?" - code: | - CREATE INDEX CONCURRENTLY idx_users_email ON users(email); + code: null follow_ups: en: - "Why is regular CREATE INDEX problematic on big tables?" diff --git a/data/questions/database/middle/postgresql.yaml b/data/questions/database/middle/postgresql.yaml index f568410..c3eb45e 100644 --- a/data/questions/database/middle/postgresql.yaml +++ b/data/questions/database/middle/postgresql.yaml @@ -37,12 +37,7 @@ questions: text: en: "What advanced data types does PostgreSQL support? How do you use JSONB for semi-structured data?" ru: "Какие продвинутые типы данных поддерживает PostgreSQL? Как использовать JSONB для полуструктурированных данных?" - code: | - CREATE TABLE events ( - id SERIAL PRIMARY KEY, - payload JSONB - ); - SELECT payload->>'user_id' as uid FROM events WHERE payload @> '{"type": "click"}'; + code: null follow_ups: en: - "How do you index JSONB data (GIN indexes)?" diff --git a/data/questions/database/middle/redis.yaml b/data/questions/database/middle/redis.yaml index 3adb0f1..4a3bc06 100644 --- a/data/questions/database/middle/redis.yaml +++ b/data/questions/database/middle/redis.yaml @@ -13,9 +13,7 @@ questions: text: en: "How does Redis Pub/Sub work? What are its delivery guarantees?" ru: "Как работает Redis Pub/Sub? Какие гарантии доставки?" - code: | - PUBLISH channel:orders '{"id": 1}' - SUBSCRIBE channel:orders + code: null follow_ups: en: - "What happens if no subscriber is listening?" @@ -57,11 +55,7 @@ questions: text: en: "How do Redis transactions (MULTI/EXEC) work? What is WATCH?" ru: "Как работают транзакции Redis (MULTI/EXEC)? Что такое WATCH?" - code: | - WATCH balance:1 - MULTI - DECRBY balance:1 100 - EXEC + code: null follow_ups: en: - "What happens if watched key changes before EXEC?" @@ -103,9 +97,7 @@ questions: text: en: "What happens when Redis reaches maxmemory? Explain eviction policies." ru: "Что происходит при достижении maxmemory в Redis? Объясните eviction policies." - code: | - maxmemory 256mb - maxmemory-policy allkeys-lru + code: null follow_ups: en: - "What is the difference between volatile-lru and allkeys-lru?" @@ -147,9 +139,7 @@ questions: text: en: "What are Redis Streams? How do consumer groups work?" ru: "Что такое Redis Streams? Как работают consumer groups?" - code: | - XADD events * user_id 1 action login - XREADGROUP GROUP workers consumer1 STREAMS events > + code: null follow_ups: en: - "How is XACK used?" diff --git a/data/questions/database/middle/sql-advanced.yaml b/data/questions/database/middle/sql-advanced.yaml index aadd386..e561dff 100644 --- a/data/questions/database/middle/sql-advanced.yaml +++ b/data/questions/database/middle/sql-advanced.yaml @@ -13,13 +13,7 @@ questions: text: en: "How do window functions work in SQL? Explain ROW_NUMBER, RANK, and SUM with OVER." ru: "Как работают оконные функции в SQL? Объясните ROW_NUMBER, RANK и SUM с OVER." - code: | - SELECT - department, - salary, - ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) as rank, - AVG(salary) OVER (PARTITION BY department) as dept_avg - FROM employees; + code: null follow_ups: en: - "What is the difference between ROW_NUMBER, RANK, and DENSE_RANK?" @@ -43,15 +37,7 @@ questions: text: en: "What are Common Table Expressions (CTEs) and recursive queries in SQL?" ru: "Что такое обобщённые табличные выражения (CTE) и рекурсивные запросы в SQL?" - code: | - WITH dept_stats AS ( - SELECT department_id, COUNT(*) as emp_count, AVG(salary) as avg_sal - FROM employees - GROUP BY department_id - ) - SELECT d.name, s.emp_count, s.avg_sal - FROM departments d - JOIN dept_stats s ON d.id = s.department_id; + code: null follow_ups: en: - "What is the difference between CTE and subquery?" @@ -75,8 +61,7 @@ questions: text: en: "What is the difference between correlated and non-correlated subqueries? When is EXISTS preferred over IN?" ru: "В чём разница между коррелированными и некоррелированными подзапросами? Когда EXISTS предпочтительнее IN?" - code: | - -- Find departments with employees above average salary + code: null follow_ups: en: - "What is the difference between IN and EXISTS in terms of performance?" @@ -149,15 +134,7 @@ questions: text: en: "What are views and materialized views in SQL? When would you use each?" ru: "Что такое представления (views) и материализованные представления в SQL? Когда что использовать?" - code: | - CREATE VIEW active_users AS - SELECT * FROM users WHERE status = 'active'; - - CREATE MATERIALIZED VIEW monthly_sales AS - SELECT date_trunc('month', order_date) as month, - SUM(amount) as total - FROM orders - GROUP BY month; + code: null follow_ups: en: - "Can you INSERT/UPDATE through a view?" diff --git a/data/questions/database/senior/clickhouse-analytics.yaml b/data/questions/database/senior/clickhouse-analytics.yaml index 54ac3e5..10143de 100644 --- a/data/questions/database/senior/clickhouse-analytics.yaml +++ b/data/questions/database/senior/clickhouse-analytics.yaml @@ -34,14 +34,7 @@ questions: text: en: "What is the MergeTree engine family in ClickHouse?" ru: "Что такое семейство движков MergeTree в ClickHouse?" - code: | - CREATE TABLE events ( - event_date Date, - user_id UInt64, - event_type String - ) ENGINE = MergeTree() - PARTITION BY toYYYYMM(event_date) - ORDER BY (event_date, user_id); + code: null follow_ups: en: - "What is ORDER BY used for in MergeTree?" @@ -83,11 +76,7 @@ questions: text: en: "Why are aggregate queries fast in ClickHouse? What is vectorized execution?" ru: "Почему агрегатные запросы быстры в ClickHouse? Что такое vectorized execution?" - code: | - SELECT event_type, count() - FROM events - WHERE event_date >= today() - 7 - GROUP BY event_type; + code: null follow_ups: en: - "How does columnar storage help SUM/COUNT?" diff --git a/data/questions/docker/junior/fundamentals.yaml b/data/questions/docker/junior/fundamentals.yaml index 602e055..291c10f 100644 --- a/data/questions/docker/junior/fundamentals.yaml +++ b/data/questions/docker/junior/fundamentals.yaml @@ -17,23 +17,7 @@ questions: and registries (Docker Hub). ru: Что такое Docker? Чем контейнер отличается от виртуальной машины (ВМ)? Объясните концепции образов, контейнеров и регистри (Docker Hub). - code: '# Build image from Dockerfile - - docker build -t myapp:latest . - - - # Run container - - docker run -d -p 80:80 --name myapp myapp:latest - - - # Push to registry - - docker tag myapp:latest myuser/myapp:v1 - - docker push myuser/myapp:v1 - - ' + code: null follow_ups: en: - What are the main namespaces and cgroups used by Docker? @@ -63,33 +47,7 @@ questions: the difference between CMD and ENTRYPOINT? Show a multi-stage build example.' ru: 'Объясните ключевые инструкции Dockerfile: FROM, RUN, COPY, ADD, CMD, ENTRYPOINT, EXPOSE, ENV, WORKDIR. В чём разница между CMD и ENTRYPOINT? Приведите пример многоступенчатой сборки.' - code: '# Multi-stage build example - - FROM golang:1.20 AS builder - - WORKDIR /app - - COPY . . - - RUN go build -o myapp . - - - FROM alpine:latest - - WORKDIR /root/ - - COPY --from=builder /app/myapp . - - CMD ["./myapp"] - - - # Difference: ENTRYPOINT defines executable, CMD provides defaults - - ENTRYPOINT ["python"] - - CMD ["app.py"] # user can override CMD, but ENTRYPOINT stays - - ' + code: null follow_ups: en: - Why is COPY preferred over ADD in most cases? @@ -120,28 +78,7 @@ questions: (creation, backup, removal). When to use named volumes vs bind mounts? ru: Как Docker работает с постоянными данными? Сравните тома (volumes), bind-монтирования и tmpfs-монтирования. Объясните жизненный цикл тома (создание, резервное копирование, удаление). Когда использовать именованные тома вместо bind-монтирований? - code: '# Named volume - - docker volume create mydata - - docker run -v mydata:/data myapp - - - # Bind mount (host directory) - - docker run -v /host/path:/container/path myapp - - - # tmpfs (in-memory, not persisted) - - docker run --tmpfs /tmp myapp - - - # Backup volume - - docker run --rm -v mydata:/source -v $(pwd):/backup alpine cp -a /source /backup - - ' + code: null follow_ups: en: - How to share volumes between containers? diff --git a/data/questions/docker/middle/operations.yaml b/data/questions/docker/middle/operations.yaml index 536f622..9e926a1 100644 --- a/data/questions/docker/middle/operations.yaml +++ b/data/questions/docker/middle/operations.yaml @@ -19,27 +19,7 @@ questions: bridge?' ru: 'Объясните драйверы сетей Docker: bridge (по умолчанию), host, none, overlay, macvlan. Как работает общение между контейнерами на одном хосте и между разными хостами? В чём разница между пользовательским мостом и стандартным?' - code: '# Create user-defined bridge (automatic DNS resolution) - - docker network create mynet - - docker run --network=mynet --name app1 myimage - - docker run --network=mynet --name app2 myimage - - # app2 can ping app1 by name - - - # Overlay network (requires Swarm or K8s) - - docker network create -d overlay myoverlay - - - # Host network (no isolation, uses host''s interfaces) - - docker run --network=host myapp - - ' + code: null follow_ups: en: - How to expose a container's port to the host? @@ -69,11 +49,7 @@ questions: How to scale services and handle environment-specific overrides (docker-compose.override.yml)?' ru: 'Что такое Docker Compose? Объясните синтаксис версии 3: services, networks, volumes, depends_on, healthcheck, environment. Как масштабировать сервисы и обрабатывать переопределения для разных окружений (docker-compose.override.yml)?' - code: "# docker-compose.yml\nversion: '3.8'\nservices:\n web:\n build: .\n ports: [\"80:80\"]\n depends_on:\ - \ [redis, db]\n healthcheck: {test: [\"CMD\", \"curl\", \"-f\", \"http://localhost\"], interval: 30s}\n redis:\n\ - \ image: redis:alpine\n volumes: [redis_data:/data]\n db:\n image: postgres:13\n environment: {POSTGRES_PASSWORD:\ - \ secret}\n\nvolumes: {redis_data:}\nnetworks: {default: {driver: bridge}}\n\n# Scale web to 3 replicas\ndocker compose\ - \ up --scale web=3\n" + code: null follow_ups: en: - What is the difference between depends_on and healthcheck + condition? @@ -104,10 +80,7 @@ questions: events. What logging drivers are available (json-file, syslog, journald, fluentd)? How to limit log size?' ru: 'Как инспектировать и отлаживать контейнеры Docker? Объясните: docker logs, docker exec, docker stats, docker top, docker events. Какие драйверы логирования доступны (json-file, syslog, journald, fluentd)? Как ограничить размер логов?' - code: "# View logs\ndocker logs -f --tail 100 container_name\n\n# Execute inside container\ndocker exec -it container_name\ - \ /bin/bash\n\n# Resource usage\ndocker stats container_name\n\n# Real-time events from daemon\ndocker events --filter\ - \ 'event=die'\n\n# Limit log size in daemon.json\n{\n \"log-driver\": \"json-file\",\n \"log-opts\": {\"max-size\"\ - : \"10m\", \"max-file\": \"3\"}\n}\n" + code: null follow_ups: en: - How to get logs of a stopped container? diff --git a/data/questions/docker/senior/security.yaml b/data/questions/docker/senior/security.yaml index 85b68a6..8a201ab 100644 --- a/data/questions/docker/senior/security.yaml +++ b/data/questions/docker/senior/security.yaml @@ -19,28 +19,7 @@ questions: ru: 'Каковы лучшие практики безопасности Docker? Объясните: запуск от non-root пользователя, удаление возможностей Linux (capabilities), профили seccomp, AppArmor/SELinux и rootless Docker. Как сканировать образы на уязвимости (например, Trivy, Docker Scout)?' - code: '# Dockerfile: create non-root user - - FROM python:3.11 - - RUN useradd -m appuser - - USER appuser - - - # Run container with dropped capabilities - - docker run --cap-drop=ALL --cap-add=NET_ADMIN myapp - - - # Seccomp profile (disable certain syscalls) - - docker run --security-opt seccomp=/path/to/profile.json myapp - - - # Rootless Docker installation (daemon runs without root) - - ' + code: null follow_ups: en: - What is the difference between --privileged and adding specific capabilities? diff --git a/data/questions/kafka/junior/fundamentals.yaml b/data/questions/kafka/junior/fundamentals.yaml index fa32fb5..d43d281 100644 --- a/data/questions/kafka/junior/fundamentals.yaml +++ b/data/questions/kafka/junior/fundamentals.yaml @@ -14,13 +14,7 @@ questions: text: en: What is Apache Kafka? What problems does it solve? Compare with traditional message brokers (RabbitMQ, ActiveMQ). ru: Что такое Apache Kafka? Какие проблемы решает? Сравните с традиционными брокерами сообщений (RabbitMQ, ActiveMQ). - code: '# Traditional queue: message deleted after consumption - - # Kafka: messages persisted for a retention period - - # Kafka use cases: event sourcing, log aggregation, stream processing, metrics - - ' + code: null follow_ups: en: - When would you NOT use Kafka? @@ -49,20 +43,7 @@ questions: parallelism? ru: Объясните topic, partition, offset, broker, replica, leader, ISR (In-Sync Replicas). Как партиционирование влияет на параллелизм? - code: '# Topic "orders" with 3 partitions - - # Partition 0: offsets 0,1,2,3... - - # Partition 1: offsets 0,1,2... - - # Partition 2: offsets 0,1... - - - # Consumer group reading in parallel: each partition assigned to one consumer - - # Max parallelism = number of partitions - - ' + code: null follow_ups: en: - What is an offset? Can I rewind to a previous offset? diff --git a/data/questions/kafka/middle/messaging.yaml b/data/questions/kafka/middle/messaging.yaml index 0f225e0..2b1cd01 100644 --- a/data/questions/kafka/middle/messaging.yaml +++ b/data/questions/kafka/middle/messaging.yaml @@ -17,19 +17,7 @@ questions: semantics (EOS) in Kafka? ru: Какие семантики доставки предоставляет Kafka (at-most-once, at-least-once, exactly-once)? Как достичь exactly-once semantics (EOS) в Kafka? - code: '# Exactly-once with idempotent producer + transactions - - producer.init_transactions() - - producer.begin_transaction() - - producer.send(''topic1'', value) - - producer.send(''topic2'', value) - - producer.commit_transaction() - - ' + code: null follow_ups: en: - What is idempotent producer? @@ -60,16 +48,7 @@ questions: vs eager rebalancing? How to avoid 'rebalance storm'? ru: Что такое ребалансировка consumer group? Опишите протокол ребалансировки (JoinGroup, SyncGroup). В чём разница между cooperative и eager ребалансировкой? Как избежать 'rebalance storm'? - code: '# Eager rebalancing: all consumers revoke partitions, pause consumption - - # Cooperative rebalancing (incremental): only some partitions move - - - # Session.timeout.ms and max.poll.interval.ms - - # If processing too long -> consumer leaves group -> rebalance - - ' + code: null follow_ups: en: - What happens if a consumer dies without leaving the group? @@ -98,23 +77,7 @@ questions: latency? What is min.insync.replicas? ru: Что такое параметр acks у продюсера Kafka? Объясните acks=0, acks=1, acks=all. Как это влияет на сохранность и задержку? Что такое min.insync.replicas? - code: '# Producer config - - acks=0 # fire-and-forget - - acks=1 # leader acknowledgement only - - acks=all # wait for ISR replicas - - - # Broker config - - min.insync.replicas=2 # need at least 2 replicas in ISR - - - # Guarantee: with acks=all and min.insync.replicas=2 -> data written to at least 2 brokers - - ' + code: null follow_ups: en: - What happens if min.insync.replicas cannot be satisfied? diff --git a/data/questions/kafka/middle/storage.yaml b/data/questions/kafka/middle/storage.yaml index 22c0f46..28c6181 100644 --- a/data/questions/kafka/middle/storage.yaml +++ b/data/questions/kafka/middle/storage.yaml @@ -17,23 +17,7 @@ questions: compaction? What is the difference between deletion and compaction? ru: Как Kafka хранит сообщения на диске? Что такое сегменты лога, rolling, политики удержания (время, размер) и compaction? В чём разница между удалением и компакцией? - code: '# Retention configuration - - log.retention.hours=168 # 7 days - - log.retention.bytes=1073741824 # 1GB per partition - - - # Compaction: keep latest value per key - - log.cleanup.policy=compact # for changelog topics - - - # Deletion: remove old records - - log.cleanup.policy=delete - - ' + code: null follow_ups: en: - What is a segment? Why not one big file? diff --git a/data/questions/kafka/senior/architecture.yaml b/data/questions/kafka/senior/architecture.yaml index f9bf106..e4b19e1 100644 --- a/data/questions/kafka/senior/architecture.yaml +++ b/data/questions/kafka/senior/architecture.yaml @@ -18,18 +18,7 @@ questions: Explain the new controller quorum and metadata topic. ru: Какова была роль ZooKeeper в Kafka? Какие проблемы он вызывал? Как KRaft (Kafka Raft) заменяет ZooKeeper? Объясните новый кворум контроллеров и мета-топик. - code: '# Old: ZooKeeper for broker metadata, controller election, cluster membership - - # New: KRaft - controllers run Raft consensus, store metadata in internal topic @metadata - - - # KRaft mode config - - process.roles=broker,controller - - controller.quorum.voters=1@k1:9093,2@k2:9093,3@k3:9093 - - ' + code: null follow_ups: en: - What is the metadata topic and how is it replicated? @@ -60,20 +49,7 @@ questions: and windowed joins. When to use ksqlDB vs Kafka Streams? ru: Что такое Kafka Streams и ksqlDB? Чем они отличаются от обычных консьюмеров? Объясните KStream, KTable, GlobalKTable и оконные джойны. Когда использовать ksqlDB вместо Kafka Streams? - code: '# Kafka Streams: Java library (also Python via Faust or streamz) - - KStream orders = builder.stream("orders"); - - KTable users = builder.table("users"); - - orders.join(users, (order, user) -> order.withUserInfo(user)); - - - # ksqlDB: SQL engine on Kafka - - SELECT user_id, COUNT(*) FROM orders WINDOW TUMBLING (SIZE 1 MINUTE) GROUP BY user_id; - - ' + code: null follow_ups: en: - What is state store and changelog topic? diff --git a/data/questions/kubernetes/junior/fundamentals.yaml b/data/questions/kubernetes/junior/fundamentals.yaml index 89431c3..3740876 100644 --- a/data/questions/kubernetes/junior/fundamentals.yaml +++ b/data/questions/kubernetes/junior/fundamentals.yaml @@ -18,8 +18,7 @@ questions: manager) and node components (kubelet, kube-proxy, container runtime). What is a Pod? ru: Опишите архитектуру Kubernetes. Объясните компоненты control plane (API server, etcd, scheduler, controller manager) и компоненты узла (kubelet, kube-proxy, container runtime). Что такое Pod? - code: "# Minimal Pod definition\napiVersion: v1\nkind: Pod\nmetadata:\n name: mypod\nspec:\n containers:\n - name:\ - \ nginx\n image: nginx:latest\n ports:\n - containerPort: 80\n" + code: null follow_ups: en: - What is the purpose of the scheduler? @@ -48,12 +47,7 @@ questions: hooks, and liveness/readiness/startup probes? How to define them? ru: Объясните фазы жизненного цикла Pod (Pending, Running, Succeeded, Failed, Unknown). Что такое init-контейнеры, хуки postStart/preStop и пробы liveness/readiness/startup? Как их определить? - code: "apiVersion: v1\nkind: Pod\nmetadata:\n name: probe-demo\nspec:\n initContainers:\n - name: init-myservice\n\ - \ image: busybox\n command: ['sh', '-c', 'echo \"init\"']\n containers:\n - name: app\n image: nginx\n \ - \ livenessProbe:\n httpGet: {path: /healthz, port: 80}\n initialDelaySeconds: 15\n periodSeconds: 20\n\ - \ readinessProbe:\n exec: {command: [\"cat\", \"/tmp/ready\"]}\n initialDelaySeconds: 5\n lifecycle:\n\ - \ postStart: {exec: {command: [\"/bin/sh\", \"-c\", \"echo started\"]}}\n preStop: {exec: {command: [\"/bin/sh\"\ - , \"-c\", \"nginx -s quit\"]}}\n" + code: null follow_ups: en: - What happens if liveness probe fails? @@ -85,10 +79,7 @@ questions: each? How does a RollingUpdate differ from Recreate strategy?' ru: 'Объясните ресурсы рабочих нагрузок Kubernetes: Deployment, ReplicaSet, StatefulSet, DaemonSet, Job, CronJob. Когда использовать каждый? Чем отличается стратегия RollingUpdate от Recreate?' - code: "# RollingUpdate vs Recreate\nspec:\n strategy:\n type: RollingUpdate\n rollingUpdate:\n maxSurge: 25%\n\ - \ maxUnavailable: 25%\n\n# StatefulSet with stable network identity\napiVersion: apps/v1\nkind: StatefulSet\nspec:\n\ - \ serviceName: \"my-service\"\n replicas: 3\n template: ...\n\n# DaemonSet (one pod per node)\nkind: DaemonSet\n\ - spec: ...\n" + code: null follow_ups: en: - Why StatefulSet needs a headless service? @@ -118,12 +109,7 @@ questions: or volume mounts? What are the limitations of Secrets (size, etc.)? How to update config without restarting pods? ru: Как управлять конфигурацией в Kubernetes? Объясните ConfigMap и Secret. Как внедрить их как переменные окружения или смонтировать как тома? Каковы ограничения Secret (размер и т.д.)? Как обновить конфиг без перезапуска подов? - code: "# ConfigMap from literal\nkubectl create configmap app-config --from-literal=key1=value1\n\n# Use as volume (auto-updates\ - \ when ConfigMap changes, but pod may not notice)\nspec:\n volumes:\n - name: config\n configMap: {name: app-config}\n\ - \ containers:\n - volumeMounts: [{mountPath: /etc/config, name: config}]\n\n# Use as environment variable (static,\ - \ requires pod restart)\nspec:\n containers:\n - env:\n - name: KEY1\n valueFrom:\n configMapKeyRef:\ - \ {name: app-config, key: key1}\n\n# Secret (base64 encoded, but not encrypted at rest by default)\nkubectl create secret\ - \ generic db-secret --from-literal=password=s3cret\n" + code: null follow_ups: en: - How to encrypt Secrets at rest in etcd? diff --git a/data/questions/kubernetes/middle/networking.yaml b/data/questions/kubernetes/middle/networking.yaml index e3bc119..dcc2d1f 100644 --- a/data/questions/kubernetes/middle/networking.yaml +++ b/data/questions/kubernetes/middle/networking.yaml @@ -18,9 +18,7 @@ questions: How does Ingress differ from LoadBalancer? Describe kube-proxy modes (iptables, IPVS).' ru: 'Объясните типы Service в Kubernetes: ClusterIP, NodePort, LoadBalancer, ExternalName. Что такое headless service? Чем Ingress отличается от LoadBalancer? Опишите режимы kube-proxy (iptables, IPVS).' - code: "# Headless service (no cluster IP)\nspec:\n clusterIP: None\n selector:\n app: myapp\n\n# Ingress example\n\ - apiVersion: networking.k8s.io/v1\nkind: Ingress\nspec:\n rules:\n - host: example.com\n http:\n paths:\n \ - \ - path: /api\n backend:\n service:\n name: api-service\n port: 80\n" + code: null follow_ups: en: - How to expose a TCP/UDP service via Ingress (e.g., database)? @@ -50,11 +48,7 @@ questions: volume modes (Filesystem, Block). What are the reclaim policies (Retain, Delete, Recycle)? How to use CSI drivers? ru: Как работает хранение в Kubernetes? Объясните PersistentVolume (PV), PersistentVolumeClaim (PVC), StorageClass и режимы томов (Filesystem, Block). Что такое политики возврата (Retain, Delete, Recycle)? Как использовать CSI драйверы? - code: "# StorageClass for dynamic provisioning\napiVersion: storage.k8s.io/v1\nkind: StorageClass\nmetadata: {name: fast}\n\ - provisioner: kubernetes.io/aws-ebs\nparameters: {type: gp3}\n\n# PVC requesting storage\nkind: PersistentVolumeClaim\n\ - spec:\n accessModes: [ReadWriteOnce]\n resources: {requests: {storage: 10Gi}}\n storageClassName: fast\n\n# Pod using\ - \ PVC\nspec:\n volumes:\n - name: data\n persistentVolumeClaim: {claimName: mypvc}\n containers:\n - volumeMounts:\ - \ [{mountPath: /data, name: data}]\n" + code: null follow_ups: en: - What is the difference between ReadWriteOnce, ReadOnlyMany, ReadWriteMany? diff --git a/data/questions/kubernetes/middle/scheduling.yaml b/data/questions/kubernetes/middle/scheduling.yaml index ed4102f..e429525 100644 --- a/data/questions/kubernetes/middle/scheduling.yaml +++ b/data/questions/kubernetes/middle/scheduling.yaml @@ -18,12 +18,7 @@ questions: taints and tolerations. What is the role of priorityClassName and preemption? ru: Как работает планирование подов в Kubernetes? Объясните nodeSelector, nodeAffinity (required/preferred), podAffinity/antiaffinity, taints и tolerations. Какова роль priorityClassName и вытеснения (preemption)? - code: "# Node affinity (hard requirement)\naffinity:\n nodeAffinity:\n requiredDuringSchedulingIgnoredDuringExecution:\n\ - \ nodeSelectorTerms:\n - matchExpressions:\n - key: disktype\n operator: In\n values:\ - \ [ssd]\n\n# Taint on node\nkubectl taint nodes node1 key=value:NoSchedule\n\n# Toleration on pod\ntolerations:\n- key:\ - \ key\n operator: Equal\n value: value\n effect: NoSchedule\n\n# Pod anti-affinity (avoid co-location)\npodAntiAffinity:\n\ - \ preferredDuringSchedulingIgnoredDuringExecution:\n - podAffinityTerm:\n labelSelector: {matchLabels: {app:\ - \ web}}\n topologyKey: kubernetes.io/hostname\n" + code: null follow_ups: en: - What is the difference between requiredDuringScheduling and preferredDuringScheduling? @@ -54,11 +49,7 @@ questions: ru: 'Объясните автоматическое масштабирование в Kubernetes: Horizontal Pod Autoscaler (HPA) на основе CPU/памяти и пользовательских метрик. Что такое Vertical Pod Autoscaler (VPA)? Как работает Cluster Autoscaler? Каковы предварительные требования (metrics-server)?' - code: "# HPA based on CPU\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nspec:\n scaleTargetRef: {apiVersion:\ - \ apps/v1, kind: Deployment, name: myapp}\n minReplicas: 2\n maxReplicas: 10\n metrics:\n - type: Resource\n \ - \ resource: {name: cpu, target: {type: Utilization, averageUtilization: 50}}\n\n# HPA with custom metric (Prometheus\ - \ adapter)\n- type: Pods\n pods: {metric: {name: http_requests_per_sec}, target: {type: AverageValue, averageValue:\ - \ 100}}\n\n# Cluster Autoscaler (cloud provider)\n# Adds/removes nodes based on pending pods\n" + code: null follow_ups: en: - What is the difference between averageUtilization and averageValue? diff --git a/data/questions/kubernetes/senior/production.yaml b/data/questions/kubernetes/senior/production.yaml index 0202c96..3b8d361 100644 --- a/data/questions/kubernetes/senior/production.yaml +++ b/data/questions/kubernetes/senior/production.yaml @@ -18,10 +18,7 @@ questions: TLS termination with cert-manager and Let''s Encrypt? Explain Ingress rules (path types: Exact, Prefix, ImplementationSpecific).' ru: 'Что такое Ingress Controller? Сравните NGINX Ingress, Traefik и AWS ALB Ingress Controller. Как реализовать терминирование TLS с cert-manager и Let''s Encrypt? Объясните правила Ingress (типы путей: Exact, Prefix, ImplementationSpecific).' - code: "# Ingress with TLS (cert-manager annotation)\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n annotations:\n\ - \ cert-manager.io/cluster-issuer: letsencrypt-prod\nspec:\n tls:\n - hosts: [example.com]\n secretName: example-tls\n\ - \ rules:\n - host: example.com\n http:\n paths:\n - path: /api\n pathType: Prefix\n backend:\ - \ {service: {name: api, port: {number: 80}}}\n" + code: null follow_ups: en: - How to route WebSocket traffic via Ingress? @@ -53,12 +50,7 @@ questions: ru: 'Объясните безопасность Kubernetes: RBAC (Roles, ClusterRoles, RoleBindings, ClusterRoleBindings), ServiceAccounts, PodSecurityStandards (PSP устарел) и PodSecurity admission. Как ограничить привилегии контейнера (securityContext: runAsNonRoot, allowPrivilegeEscalation, readOnlyRootFilesystem)?' - code: "# Role (namespace-scoped)\nkind: Role\nrules:\n- apiGroups: [\"\"]\n resources: [\"pods\"]\n verbs: [\"get\"\ - , \"list\"]\n\n# RoleBinding\nkind: RoleBinding\nsubjects:\n- kind: ServiceAccount\n name: my-sa\nroleRef: {kind: Role,\ - \ name: pod-reader}\n\n# Pod security context\nspec:\n securityContext:\n runAsNonRoot: true\n runAsUser: 1000\n\ - \ containers:\n - name: app\n securityContext:\n allowPrivilegeEscalation: false\n readOnlyRootFilesystem:\ - \ true\n\n# PodSecurity admission (enforce baseline)\nnamespace: default\nlabels:\n pod-security.kubernetes.io/enforce:\ - \ baseline\n" + code: null follow_ups: en: - What is the difference between Role and ClusterRole? diff --git a/data/questions/observability/junior/logging.yaml b/data/questions/observability/junior/logging.yaml index c277559..d952ed1 100644 --- a/data/questions/observability/junior/logging.yaml +++ b/data/questions/observability/junior/logging.yaml @@ -17,12 +17,7 @@ questions: compactor, index (boltdb-shipper or TSDB). What is the role of Promtail (or Grafana Agent)?' ru: 'Что такое Loki? Чем он отличается от Elasticsearch (ELK)? Объясните архитектуру Loki: distributor, ingester, querier, compactor, индекс (boltdb-shipper или TSDB). Какова роль Promtail (или Grafana Agent)?' - code: "# Simple loki-config.yaml\nauth_enabled: false\nserver:\n http_listen_port: 3100\ningester:\n lifecycler:\n \ - \ ring: {kvstore: {store: inmemory}}\nstorage_config:\n boltdb_shipper: {active_index_directory: /loki/index}\n \ - \ filesystem: {directory: /loki/chunks}\nschema_config:\n configs:\n - from: 2020-01-01\n store: boltdb-shipper\n\ - \ object_store: filesystem\n schema: v11\n index: {prefix: index_, period: 24h}\n\n# Promtail config\ - \ to tail logs and send to Loki\nscrape_configs:\n - job_name: system\n static_configs:\n - targets: [localhost]\n\ - \ labels: {job: syslog, __path__: /var/log/*.log}\n" + code: null follow_ups: en: - Why doesn't Loki do full-text indexing like Elasticsearch? @@ -54,11 +49,7 @@ questions: ru: Как интегрировать Loki с Grafana? Покажите, как добавить источник данных Loki, использовать Explore для запросов логов и создать дашборд с панелью логов. Как переключаться с логов на метрики (и обратно) с помощью производных полей (извлечение trace_id для связи с Tempo). - code: "# Grafana datasource config (YAML provisioning)\napiVersion: 1\ndatasources:\n - name: Loki\n type: loki\n\ - \ url: http://loki:3100\n jsonData:\n maxLines: 1000\n derivedFields:\n - name: trace_id\n \ - \ matcherRegex: \"trace_id=(\\\\w+)\"\n url: \"$${__value.raw}\"\n datasourceUid: tempo\n\n\ - # Logs panel query in dashboard\n{namespace=\"prod\"} |= \"error\"\n\n# Switch to metrics (LogQL to metric query)\n\ - sum(count_over_time({namespace=\"prod\"} |= \"error\" [5m]))\n" + code: null follow_ups: en: - How to create an alert from logs (e.g., more than 5 errors per minute)? diff --git a/data/questions/observability/junior/prometheus.yaml b/data/questions/observability/junior/prometheus_questions.yaml similarity index 75% rename from data/questions/observability/junior/prometheus.yaml rename to data/questions/observability/junior/prometheus_questions.yaml index f0e3d85..68d29ba 100644 --- a/data/questions/observability/junior/prometheus.yaml +++ b/data/questions/observability/junior/prometheus_questions.yaml @@ -17,9 +17,7 @@ questions: Pushgateway, Alertmanager) exist and what are their roles? ru: Объясните архитектуру Prometheus. Как работает pull-модель? Какие компоненты существуют (сервер Prometheus, TSDB, экспортеры, Pushgateway, Alertmanager) и какова их роль? - code: "# Prometheus configuration (prometheus.yml) scraping itself\nscrape_configs:\n - job_name: 'prometheus'\n static_configs:\n\ - \ - targets: ['localhost:9090']\n\n# Pushgateway example (for short-lived jobs)\necho \"some_metric 42\" | curl\ - \ --data-binary @- http://pushgateway:9091/metrics/job/my_job\n" + code: null follow_ups: en: - Why does Prometheus use pull instead of push? @@ -50,36 +48,7 @@ questions: and Summary? Provide example metrics with _total, _bucket suffixes. ru: Какие типы метрик поддерживает Prometheus? Объясните Counter, Gauge, Histogram, Summary. Как выбрать между Histogram и Summary? Приведите примеры метрик с суффиксами _total, _bucket. - code: '# Counter (only increases, use rate()) - - http_requests_total{method="GET", status="200"} 12345 - - - # Gauge (goes up and down) - - cpu_temperature_celsius 36.5 - - - # Histogram (observations in buckets, +_sum, +_count) - - http_request_duration_seconds_bucket{le="0.1"} 100 - - http_request_duration_seconds_bucket{le="0.5"} 250 - - http_request_duration_seconds_sum 125.3 - - http_request_duration_seconds_count 300 - - - # Summary (quantiles client-side) - - rpc_duration_seconds{quantile="0.9"} 0.23 - - rpc_duration_seconds_sum 7.8 - - rpc_duration_seconds_count 100 - - ' + code: null follow_ups: en: - What is the difference between Histogram and Summary quantiles? diff --git a/data/questions/observability/junior/visualization.yaml b/data/questions/observability/junior/visualization.yaml index 301f52d..b7f1d9a 100644 --- a/data/questions/observability/junior/visualization.yaml +++ b/data/questions/observability/junior/visualization.yaml @@ -17,10 +17,7 @@ questions: to create a simple graph panel with a Prometheus query and set basic visualization options (legend, axes, thresholds)? ru: Что такое Grafana? Объясните концепцию дашбордов, панелей, строк и источников данных (Prometheus, Loki и др.). Как создать простой график с Prometheus-запросом и настроить базовые параметры визуализации (легенда, оси, пороги)? - code: "# Dashboard JSON model (excerpt)\n{\n \"title\": \"My Dashboard\",\n \"panels\": [\n {\n \"title\": \"\ - CPU Usage\",\n \"targets\": [\n { \"expr\": \"rate(node_cpu_seconds_total{mode='user'}[5m])\", \"legendFormat\"\ - : \"user\" }\n ],\n \"thresholds\": [\n { \"color\": \"red\", \"value\": 80, \"op\": \"gt\" }\n \ - \ ]\n }\n ]\n}\n" + code: null follow_ups: en: - What is the difference between Stat, Gauge, and Time series panels? @@ -51,30 +48,7 @@ questions: ru: 'Как использовать переменные Grafana (шаблонизация)? Объясните типы переменных: query, interval, custom, constant. Покажите пример переменной, которая получает все экземпляры из Prometheus, и используйте её в запросе панели. Как использовать $__rate_interval и $__interval?' - code: '# Variable of type Query - - Name: instance - - Query label_values(node_cpu_seconds_total, instance) - - - # Panel query using variable - - rate(node_cpu_seconds_total{instance=~"$instance", mode="user"}[$__rate_interval]) - - - # Interval variable (auto-adjusted) - - rate(http_requests_total[$__interval]) - - - # Common system variables - - $__rate_interval: adjusted for proper rate alignment (>= $__interval, at least 4x scrape interval) - - $__interval: automatically chosen based on time range and screen width - - ' + code: null follow_ups: en: - What is the difference between $__interval and $__rate_interval? @@ -103,32 +77,7 @@ questions: Clock, or Infinity data source)? What are the risks of using custom plugins? ru: Какие типы плагинов существуют в Grafana (источники данных, панели, приложения)? Как установить плагин сообщества (например, Pie Chart, Clock или Infinity data source)? Каковы риски использования пользовательских плагинов? - code: '# Install plugin via CLI - - grafana-cli plugins install grafana-piechart-panel - - - # Install from environment variable - - GF_INSTALL_PLUGINS=grafana-piechart-panel,grafana-clock-panel - - - # Provision plugin in Grafana config - - [plugins] - - allow_loading_unsigned_plugins = my-custom-panel - - - # Example of using Infinity data source (query REST APIs) - - type: infinity - - url: https://api.example.com/data - - source: json - - ' + code: null follow_ups: en: - How to create a custom panel plugin? diff --git a/data/questions/observability/middle/logging.yaml b/data/questions/observability/middle/logging.yaml index 08a29f6..89293f8 100644 --- a/data/questions/observability/middle/logging.yaml +++ b/data/questions/observability/middle/logging.yaml @@ -19,40 +19,7 @@ questions: ru: 'Напишите LogQL запросы: (a) отфильтровать все логи с ''error'' в сообщении, (b) показать количество ошибок в минуту, (c) распарсить JSON лог и извлечь поле, (d) рассчитать rate ошибок за 5 минут. Объясните фильтры строк (=, !=, =~, !~) и сопоставители меток.' - code: '# (a) simple filter - - {job="app"} |= "error" - - - # (b) count of error logs per minute - - sum(count_over_time({job="app"} |= "error" [1m])) - - - # (c) parse JSON and extract status field - - {job="app"} | json | line_format "Status: {{.status}}" - - - # (d) rate of error logs per second - - rate({job="app"} |= "error" [5m]) - - - # Line filters - - {app="myapp"} != "debug" # exclude debug - - {app="myapp"} =~ "(?i)error|fatal" # regex case-insensitive - - {app="myapp"} !~ "INFO.*user" # negative regex - - - # Label matchers - - {namespace="prod", app=~"web-.*"} # regex on label - - ' + code: null follow_ups: en: - What is the difference between |= and |~? @@ -82,11 +49,7 @@ questions: ru: Как работает мультитенантность в Loki? Как настроить разделение тенантов через заголовок X-Scope-OrgID? Как настроить Grafana для отправки логов в конкретный тенант (через пользовательские HTTP заголовки)? Объясните, как выполнять запросы между тенантами (как администратор). - code: "# Loki auth config (enable multitenancy)\nauth_enabled: true\n\n# Promtail config with tenant ID\nclients:\n -\ - \ url: http://loki:3100/loki/api/v1/push\n tenant_id: \"tenant-1\"\n\n# Grafana data source configuration (HTTP headers)\n\ - # Custom HTTP Headers: X-Scope-OrgID = tenant-1\n\n# Admin query across all tenants (requires auth)\n# Not supported\ - \ in default Loki; use query frontend with special flag or Loki microservices mode\n# In Loki's distributor, you can\ - \ skip tenant labeling for internal queries.\n" + code: null follow_ups: en: - How to use nginx sidecar for dynamic tenant extraction? @@ -116,13 +79,7 @@ questions: as labels.' ru: 'Как использовать конвейерные этапы Promtail? Объясните docker_sd, file_targets и этапы: docker, regex, json, template, labels, output, timestamp, replace. Покажите пример извлечения level и module из строки лога и добавления их как меток.' - code: "# Promtail config with pipeline\nscrape_configs:\n - job_name: app-logs\n file_sd_configs:\n - files:\ - \ [\"/var/log/*.log\"]\n pipeline_stages:\n - regex:\n expression: 'level=(?P\\w+) module=(?P\\\ - w+)'\n - labels:\n level: \"\"\n module: \"\"\n - json:\n expressions: {time: timestamp,\ - \ msg: message}\n - timestamp:\n source: time\n format: RFC3339\n - output:\n source:\ - \ msg\n\n# Docker service discovery (logs from containers)\n - job_name: docker\n docker_sd_configs:\n - host:\ - \ unix:///var/run/docker.sock\n relabel_configs:\n - source_labels: [__meta_docker_container_name]\n \ - \ target_label: container\n" + code: null follow_ups: en: - How to drop logs or filter at pipeline level? diff --git a/data/questions/observability/middle/prometheus.yaml b/data/questions/observability/middle/prometheus_questions.yaml similarity index 67% rename from data/questions/observability/middle/prometheus.yaml rename to data/questions/observability/middle/prometheus_questions.yaml index 776ddfa..737b581 100644 --- a/data/questions/observability/middle/prometheus.yaml +++ b/data/questions/observability/middle/prometheus_questions.yaml @@ -20,35 +20,7 @@ questions: ru: 'Напишите PromQL запросы для: (a) получения текущего использования памяти, (b) вычисления per-second rate HTTP запросов за 5 минут, (c) получения 95-го перцентиля длительности запросов, (d) сравнения текущего CPU с состоянием час назад. Объясните сопоставление векторов (on, ignoring, group_left/right).' - code: '# (a) current memory usage - - node_memory_MemAvailable_bytes - - - # (b) rate of HTTP requests (per second) over 5m - - rate(http_requests_total[5m]) - - - # (c) 95th percentile request duration from histogram (over last 10m) - - histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[10m])) by (le)) - - - # (d) compare CPU usage now vs 1h ago - - node_cpu_seconds_total{mode="user"} / ignoring(mode) group_left - - (node_cpu_seconds_total{mode="user"} offset 1h) - - - # Vector matching examples: - - # on: join on specific labels - - rate(http_requests_total[5m]) * on(instance) group_left(node_name) node_info - - ' + code: null follow_ups: en: - What is the difference between rate() and irate()? @@ -79,13 +51,7 @@ questions: ru: Как мониторить инфраструктуру с помощью экспортеров Prometheus? Объясните node_exporter, blackbox_exporter и cAdvisor. Как работает service discovery в Kubernetes (kubernetes_sd_config)? Покажите relabel_configs для фильтрации и перезаписи меток. - code: "# Kubernetes service discovery config\nscrape_configs:\n - job_name: 'kubernetes-pods'\n kubernetes_sd_configs:\n\ - \ - role: pod\n relabel_configs:\n - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]\n\ - \ action: keep\n regex: true\n - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]\n\ - \ action: replace\n target_label: __metrics_path__\n regex: (.+)\n - source_labels: [__address__,\ - \ __meta_kubernetes_pod_annotation_prometheus_io_port]\n action: replace\n regex: (.+):(?:\\d+);(\\d+)\n\ - \ replacement: $1:$2\n target_label: __address__\n\n# Blackbox exporter (probe HTTP endpoint)\nmodules:\n\ - \ http_2xx:\n prober: http\n timeout: 5s\n" + code: null follow_ups: en: - What is the purpose of __address__ and __metrics_path__ labels? @@ -115,14 +81,7 @@ questions: ru: 'Как определить правила оповещения и записывающие правила Prometheus? Покажите пример alert rule (высокое использование CPU) и recording rule (агрегированный rate запросов). Объясните конфигурацию Alertmanager: маршруты, получатели, ингибирование, group_by, group_wait/group_interval/repeat_interval.' - code: "# recording rule (aggregate)\ngroups:\n - name: recording_rules\n rules:\n - record: job:http_requests:rate5m\n\ - \ expr: sum(rate(http_requests_total[5m])) by (job)\n\n# alert rule\n - name: cpu_alerts\n rules:\n \ - \ - alert: HighCPUUsage\n expr: node_cpu_seconds_total{mode=\"user\"} > 0.8\n for: 5m\n annotations:\n\ - \ summary: \"High CPU on {{ $labels.instance }}\"\n\n# Alertmanager config (alertmanager.yml)\nroute:\n group_by:\ - \ ['alertname', 'cluster']\n group_wait: 10s\n group_interval: 30s\n repeat_interval: 4h\n receiver: 'pagerduty'\n\ - \ routes:\n - match:\n severity: critical\n receiver: 'pagerduty'\nreceivers:\n - name: 'pagerduty'\n\ - \ pagerduty_configs: [ ... ]\ninhibit_rules:\n - source_match: {severity: 'critical'}\n target_match: {severity:\ - \ 'warning'}\n equal: ['instance']\n" + code: null follow_ups: en: - What is the purpose of 'for' in alert rules? diff --git a/data/questions/observability/middle/visualization.yaml b/data/questions/observability/middle/visualization.yaml index 25688f5..42e51b8 100644 --- a/data/questions/observability/middle/visualization.yaml +++ b/data/questions/observability/middle/visualization.yaml @@ -19,13 +19,7 @@ questions: ru: 'Объясните систему оповещения Grafana (unified). Чем она отличается от старых оповещений дашбордов? Опишите компоненты: запросы к источникам данных, выражения (Reduce, Math, Resample), вычисление правила оповещения, точки контакта, политики уведомлений, заглушки (silences).' - code: "# Grafana alert rule (via API or UI)\n{\n \"title\": \"High CPU Alert\",\n \"condition\": \"C\",\n \"data\"\ - : [\n { \"refId\": \"A\", \"queryType\": \"range\", \"datasourceUid\": \"prometheus\",\n \"model\": { \"expr\"\ - : \"node_cpu_seconds_total{mode='user'}\" } },\n { \"refId\": \"B\", \"type\": \"reduce\", \"reducer\": \"mean\"\ - \ },\n { \"refId\": \"C\", \"type\": \"threshold\", \"conditions\": [{ \"evaluator\": { \"type\": \"gt\", \"params\"\ - : [0.8] } }] }\n ],\n \"for\": \"5m\",\n \"annotations\": { \"summary\": \"CPU high on {{ $labels.instance }}\" }\n\ - }\n\n# Contact point (Slack)\n{\n \"type\": \"slack\",\n \"settings\": { \"url\": \"https://hooks.slack.com/...\"\ - , \"text\": \"Alert: {{ .Annotations.summary }}\" }\n}\n" + code: null follow_ups: en: - How to handle multi-dimensional alerts (one alert per series)? @@ -55,12 +49,7 @@ questions: resources using YAML files. How to set up folder structure and dashboard version control with Git? ru: Что такое provisioning в Grafana (как код)? Объясните, как настроить источники данных, дашборды (через JSON) и ресурсы оповещения с помощью YAML файлов. Как настроить структуру папок и версионирование дашбордов с Git? - code: "# provisioning/datasources/prometheus.yaml\napiVersion: 1\ndatasources:\n - name: Prometheus\n type: prometheus\n\ - \ access: proxy\n url: http://prometheus:9090\n isDefault: true\n\n# provisioning/dashboards/default.yaml\n\ - apiVersion: 1\nproviders:\n - name: 'default'\n folder: 'General'\n type: file\n options:\n path: /var/lib/grafana/dashboards\n\ - \n# provisioning/alerting/contact-points.yaml\napiVersion: 1\ncontactPoints:\n - name: slack\n receivers:\n \ - \ - type: slack\n settings:\n url: https://hooks.slack.com/...\n\n# Dashboard JSON file (exported from\ - \ UI)\n# store in git, then synced to /dashboards folder\n" + code: null follow_ups: en: - How to handle dashboard changes without overwriting user modifications? diff --git a/data/questions/observability/senior/logging.yaml b/data/questions/observability/senior/logging.yaml index cd2b874..4eea2ed 100644 --- a/data/questions/observability/senior/logging.yaml +++ b/data/questions/observability/senior/logging.yaml @@ -18,11 +18,7 @@ questions: are the advantages of TSDB index over boltdb-shipper? How to run the compactor and what does it do? ru: Как Loki хранит данные в объектном хранилище (S3, GCS, MinIO)? Объясните режимы индекса boltdb-shipper и TSDB. Каковы преимущества TSDB индекса перед boltdb-shipper? Как запустить компрактор и что он делает? - code: "# Loki storage config for S3\nstorage_config:\n boltdb_shipper:\n active_index_directory: /loki/index\n \ - \ shared_store: s3\n cache_location: /loki/cache\n aws:\n s3: s3://bucket/loki\n\n# TSDB index (Loki 2.8+)\n\ - schema_config:\n configs:\n - from: 2023-01-01\n store: tsdb\n object_store: s3\n schema: v12\n\n\ - # Compactor (single instance, handle index compaction)\ntarget: compactor\ncompactor:\n working_directory: /loki/compactor\n\ - \ retention_enabled: true\n retention_delete_delay: 2h\n" + code: null follow_ups: en: - What is the difference between boltdb-shipper and TSDB? diff --git a/data/questions/observability/senior/prometheus.yaml b/data/questions/observability/senior/prometheus_questions.yaml similarity index 73% rename from data/questions/observability/senior/prometheus.yaml rename to data/questions/observability/senior/prometheus_questions.yaml index 0baa7c8..8e5d7a8 100644 --- a/data/questions/observability/senior/prometheus.yaml +++ b/data/questions/observability/senior/prometheus_questions.yaml @@ -20,11 +20,7 @@ questions: ru: Как масштабировать Prometheus для множества кластеров или долгосрочного хранения? Объясните иерархическую федерацию (плюсы/минусы), remote_write/remote_read и архитектуру Thanos/Cortex/VictoriaMetrics. Каковы ограничения федерации и когда использовать Thanos? - code: "# Federation scrape config (on central Prometheus)\nscrape_configs:\n - job_name: 'federate'\n honor_labels:\ - \ true\n metrics_path: '/federate'\n params:\n 'match[]':\n - '{__name__=~\"job:.*\"}'\n -\ - \ '{__name__=~\"node_.*\"}'\n static_configs:\n - targets: ['prometheus-east:9090', 'prometheus-west:9090']\n\ - \n# Remote write configuration\nremote_write:\n - url: \"http://thanos-receive:19291/api/v1/receive\"\n queue_config:\n\ - \ capacity: 10000\n" + code: null follow_ups: en: - What is the problem with federation for high cardinality labels? @@ -53,11 +49,7 @@ questions: library (OpenTelemetry) and querying with PromQL (exemplar $__rate_interval). ru: Что такое Exemplars в Prometheus? Как они связывают метрики с трейсами? Покажите пример внедрения exemplar в клиентской библиотеке (OpenTelemetry) и запрос с помощью PromQL (exemplar $__rate_interval). - code: "# In code (OpenTelemetry Python) - automatic exemplar injection\nfrom opentelemetry import metrics\nmeter = metrics.get_meter(\"\ - myapp\")\nrequest_counter = meter.create_counter(\"http_requests_total\")\n# With trace context automatically attached\ - \ as exemplar\n\n# PromQL query to show exemplars (Grafana)\nrate(http_requests_total[$__rate_interval])\n# In Grafana,\ - \ exemplars appear as dots on time series\n\n# Exemplar JSON (stored alongside metric)\n{\n \"trace_id\": \"4bf92f3577b34da6a3ce929d0e0e4736\"\ - ,\n \"span_id\": \"00f067aa0ba902b7\",\n \"value\": 1.23,\n \"timestamp\": 1600000000.123\n}\n" + code: null follow_ups: en: - Which storage backends support exemplars? diff --git a/data/questions/python/junior/async.yaml b/data/questions/python/junior/async.yaml index ab3905c..6f6b36b 100644 --- a/data/questions/python/junior/async.yaml +++ b/data/questions/python/junior/async.yaml @@ -14,31 +14,7 @@ questions: text: en: "What is the GIL (Global Interpreter Lock) and why is it needed? Does it affect the execution of asynchronous code?" ru: "Что такое GIL (Global Interpreter Lock) и для чего он нужен? Влияет ли он на выполнение асинхронного кода?" - code: | - # GIL prevents multiple native threads from executing Python bytecodes - # This affects CPU-bound multithreading but not I/O-bound - - import threading - import time - - # CPU-bound task (affected by GIL) - def cpu_bound(): - result = 0 - for i in range(10**7): - result += i - return result - - # I/O-bound task (less affected) - def io_bound(): - time.sleep(1) # Simulate I/O - return "done" - - # Async code (not affected in the same way) - import asyncio - - async def async_task(): - await asyncio.sleep(1) - return "async done" + code: null follow_ups: en: @@ -69,27 +45,7 @@ questions: text: en: "What are concurrency and parallelism? What problems are solved with multithreading, multiprocessing, and asynchrony?" ru: "Что такое конкурентность (concurrency) и параллельность (parallelism)? Какие задачи решаются с помощью многопоточности, многопроцессности и асинхронности?" - code: | - # Concurrency vs Parallelism - - # Concurrency: multiple tasks make progress - # (not necessarily at same time) - async def concurrent_tasks(): - task1 = asyncio.create_task(fetch_url(url1)) - task2 = asyncio.create_task(fetch_url(url2)) - await asyncio.gather(task1, task2) - - # Parallelism: multiple tasks execute simultaneously - # (requires multiple CPU cores) - from multiprocessing import Pool - - def parallel_tasks(): - with Pool(processes=4) as pool: - results = pool.map(process_data, large_dataset) - - # Multithreading: I/O-bound tasks, GIL limited - # Multiprocessing: CPU-bound tasks, bypasses GIL - # Asynchrony: I/O-bound, single-threaded concurrency + code: null follow_ups: en: @@ -120,29 +76,7 @@ questions: text: en: "What is a coroutine and how does it differ from an ordinary function?" ru: "Что такое корутина (coroutine) и чем она отличается от обычной функции?" - code: | - # Regular function - def regular_function(): - print("Start") - time.sleep(1) # Blocks entire thread - print("End") - return "done" - - # Coroutine (async function) - async def coroutine_function(): - print("Start") - await asyncio.sleep(1) # Non-blocking - print("End") - return "done" - - # Calling them differently - result = regular_function() # Direct call - - # Coroutines need to be awaited - result = await coroutine_function() - - # Or run in event loop - result = asyncio.run(coroutine_function()) + code: null follow_ups: en: @@ -174,35 +108,7 @@ questions: text: en: "What is an event loop and how does it manage the execution of asynchronous tasks?" ru: "Что такое event loop (цикл событий) и как он управляет выполнением асинхронных задач?" - code: | - import asyncio - - # Event loop manages async tasks - async def task1(): - print("Task 1 start") - await asyncio.sleep(1) - print("Task 1 end") - - async def task2(): - print("Task 2 start") - await asyncio.sleep(0.5) - print("Task 2 end") - - async def main(): - # Create tasks - t1 = asyncio.create_task(task1()) - t2 = asyncio.create_task(task2()) - - # Event loop manages execution - await asyncio.gather(t1, t2) - - # Run event loop - asyncio.run(main()) - - # Manual event loop (older style) - # loop = asyncio.get_event_loop() - # loop.run_until_complete(main()) - # loop.close() + code: null follow_ups: en: @@ -233,33 +139,7 @@ questions: text: en: "How are generators related to asynchronous code?" ru: "Как генераторы (generators) связаны с асинхронным кодом?" - code: | - # Generator (synchronous) - def count_up_to(n): - i = 0 - while i < n: - yield i # Pauses here - i += 1 - - # Using generator - for num in count_up_to(5): - print(num) # 0, 1, 2, 3, 4 - - # Async generator (Python 3.6+) - async def async_count_up_to(n): - i = 0 - while i < n: - yield i # Async pause - i += 1 - await asyncio.sleep(0.1) - - # Using async generator - async for num in async_count_up_to(5): - print(num) - - # Historical: generators were basis for async/await - # yield from -> await - # @asyncio.coroutine -> async def + code: null follow_ups: en: @@ -291,47 +171,7 @@ questions: text: en: "How to run sequential, background, and group execution of asynchronous tasks? What are their differences?" ru: "Как выполнить последовательный, фоновый и групповой запуск асинхронных задач? В чём их различия?" - code: | - import asyncio - - async def task(name, delay): - print(f"{name} starting") - await asyncio.sleep(delay) - print(f"{name} finished") - return f"{name} result" - - # Sequential execution - async def sequential(): - result1 = await task("Task1", 1) - result2 = await task("Task2", 1) - return [result1, result2] # Takes 2 seconds - - # Concurrent execution (group) - async def concurrent(): - # Using gather - results = await asyncio.gather( - task("Task1", 1), - task("Task2", 1) - ) # Takes 1 second - return results - - # Background task (fire and forget) - async def background(): - # create_task starts task without waiting - bg_task = asyncio.create_task(task("Background", 2)) - # Do other work - await asyncio.sleep(1) - print("Main work done") - # Optionally wait for background task - await bg_task - - # Wait for first completed - async def wait_first(): - done, pending = await asyncio.wait( - [task("Fast", 0.5), task("Slow", 2)], - return_when=asyncio.FIRST_COMPLETED - ) - return done, pending + code: null follow_ups: en: @@ -363,48 +203,7 @@ questions: text: en: "What is an asynchronous iterator and an asynchronous context manager?" ru: "Что такое асинхронный итератор и асинхронный контекстный менеджер?" - code: | - import asyncio - - # Async iterator - class AsyncCounter: - def __init__(self, stop): - self.current = 0 - self.stop = stop - - def __aiter__(self): - return self - - async def __anext__(self): - if self.current >= self.stop: - raise StopAsyncIteration - await asyncio.sleep(0.1) # Async operation - self.current += 1 - return self.current - 1 - - # Using async iterator - async def use_async_iterator(): - async for value in AsyncCounter(5): - print(value) - - # Async context manager - class AsyncDatabaseConnection: - async def __aenter__(self): - print("Connecting to database...") - await asyncio.sleep(0.5) - self.connection = "database_connection" - return self.connection - - async def __aexit__(self, exc_type, exc_val, exc_tb): - print("Closing database connection...") - await asyncio.sleep(0.2) - self.connection = None - - # Using async context manager - async def use_async_context(): - async with AsyncDatabaseConnection() as conn: - print(f"Using connection: {conn}") - await asyncio.sleep(1) + code: null follow_ups: en: @@ -436,54 +235,7 @@ questions: text: en: "How to limit the execution time of a coroutine (timeout)?" ru: "Как можно ограничить время выполнения корутины (тайм-аут)?" - code: | - import asyncio - - async def slow_operation(): - await asyncio.sleep(5) # Simulate slow operation - return "done" - - # Method 1: asyncio.wait_for - async def with_timeout(): - try: - result = await asyncio.wait_for(slow_operation(), timeout=2.0) - return result - except asyncio.TimeoutError: - return "timeout" - - # Method 2: asyncio.wait with timeout - async def with_wait_timeout(): - task = asyncio.create_task(slow_operation()) - try: - done, pending = await asyncio.wait([task], timeout=2.0) - if task in done: - return task.result() - else: - task.cancel() - return "timeout" - except asyncio.CancelledError: - return "cancelled" - - # Method 3: asyncio.timeout context manager (Python 3.11+) - async def with_timeout_context(): - try: - async with asyncio.timeout(2.0): - return await slow_operation() - except TimeoutError: - return "timeout" - - # Method 4: shield from cancellation - async def with_shield(): - try: - # shield protects from cancellation during cleanup - result = await asyncio.wait_for( - asyncio.shield(slow_operation()), - timeout=2.0 - ) - return result - except asyncio.TimeoutError: - # Task continues in background - return "timeout but task continues" + code: null follow_ups: en: @@ -513,18 +265,7 @@ questions: text: en: "What is the difference between asyncio.create_task() and asyncio.gather()? When would you use each?" ru: "В чём разница между asyncio.create_task() и asyncio.gather()? Когда использовать каждый?" - code: | - import asyncio - - async def fetch(url): - await asyncio.sleep(0.1) - return url - - async def main(): - t1 = asyncio.create_task(fetch("a")) - t2 = asyncio.create_task(fetch("b")) - results = await asyncio.gather(t1, t2) - return results + code: null follow_ups: en: - "What happens if one task in gather() raises an exception?" @@ -545,15 +286,7 @@ questions: text: en: "Why should you avoid time.sleep() and blocking I/O inside async def functions?" ru: "Почему нельзя вызывать time.sleep() и блокирующий I/O внутри async def?" - code: | - import asyncio - import time - - async def bad(): - time.sleep(1) # blocks entire event loop! - - async def good(): - await asyncio.sleep(1) # yields control to other tasks + code: null follow_ups: en: - "How do you call blocking code from async code safely?" @@ -574,15 +307,7 @@ questions: text: en: "What is a common junior mistake when calling async functions? Explain why 'result = my_coro()' is wrong." ru: "Какая типичная ошибка junior при вызове async-функций? Почему 'result = my_coro()' неверно?" - code: | - async def fetch_data(): - return {"ok": True} - - # Wrong: returns coroutine object, not data - result = fetch_data() - - # Correct - result = await fetch_data() + code: null follow_ups: en: - "What warning might Python show for unawaited coroutines?" diff --git a/data/questions/python/junior/basics.yaml b/data/questions/python/junior/basics.yaml index 358d97d..9a84b0f 100644 --- a/data/questions/python/junior/basics.yaml +++ b/data/questions/python/junior/basics.yaml @@ -148,21 +148,7 @@ questions: text: en: "What is the difference between an interpreter and a compiler? How is Python code executed and what processes occur when it runs?" ru: "В чём разница между интерпретатором и компилятором? Как выполняется Python-код и какие процессы происходят при его запуске?" - code: | - # Python is interpreted, but has compilation steps - - # 1. Source code (.py file) - # 2. Python compiler generates bytecode (.pyc) - # 3. Python Virtual Machine (PVM) executes bytecode - # 4. Result - - # Check bytecode - import dis - - def add(a, b): - return a + b - - print(dis.dis(add)) + code: null follow_ups: en: @@ -193,31 +179,7 @@ questions: text: en: "How does Python manage memory allocation and deallocation? How does the garbage collector work?" ru: "Каким образом Python управляет выделением и освобождением памяти? Как работает сборщик мусора (garbage collector)?" - code: | - import gc - import sys - - # Reference counting - a = [] # ref count = 1 - b = a # ref count = 2 - del b # ref count = 1 - - # Circular reference - class Node: - def __init__(self): - self.parent = None - self.children = [] - - parent = Node() - child = Node() - parent.children.append(child) - child.parent = parent # Circular reference - - # Force garbage collection - gc.collect() - - # Check GC thresholds - print(gc.get_threshold()) + code: null follow_ups: en: @@ -249,39 +211,7 @@ questions: text: en: "What are magic (dunder) methods? Which of them are called when an instance of a class is created? Which magic methods does a context manager implement (the with protocol) and why is it used?" ru: "Что такое магические (dunder) методы? Какие из них вызываются при создании экземпляра класса? Какие магические методы реализует контекстный менеджер (протокол with) и для чего он нужен?" - code: | - # Magic methods for instance creation - class Example: - def __new__(cls, *args, **kwargs): - print("__new__ called") - instance = super().__new__(cls) - return instance - - def __init__(self, value): - print("__init__ called") - self.value = value - - # Context manager protocol - class FileManager: - def __init__(self, filename, mode): - self.filename = filename - self.mode = mode - self.file = None - - def __enter__(self): - print("Opening file") - self.file = open(self.filename, self.mode) - return self.file - - def __exit__(self, exc_type, exc_val, exc_tb): - print("Closing file") - if self.file: - self.file.close() - # Return True to suppress exceptions - - # Using context manager - with FileManager("test.txt", "w") as f: - f.write("Hello") + code: null follow_ups: en: diff --git a/data/questions/python/junior/control-flow.yaml b/data/questions/python/junior/control-flow.yaml index 22abdd7..f5735d7 100644 --- a/data/questions/python/junior/control-flow.yaml +++ b/data/questions/python/junior/control-flow.yaml @@ -57,11 +57,7 @@ questions: text: en: "What are comprehensions in Python? Explain list, dict, and set comprehensions." ru: "Что такое включения (comprehensions) в Python? Объясните включения списков, словарей и множеств." - code: | - squares = [] - for x in range(10): - squares.append(x ** 2) - # Convert this into a list comprehension + code: null follow_ups: en: - "What is a generator expression and how does it differ from a list comprehension?" @@ -83,9 +79,7 @@ questions: text: en: "How does the 'with' statement work in Python? What protocol does it use?" ru: "Как работает оператор 'with' в Python? Какой протокол он использует?" - code: | - with open('file.txt', 'r') as f: - data = f.read() + code: null follow_ups: en: - "What are __enter__ and __exit__ methods?" diff --git a/data/questions/python/junior/data-structures.yaml b/data/questions/python/junior/data-structures.yaml index c288e63..80e879d 100644 --- a/data/questions/python/junior/data-structures.yaml +++ b/data/questions/python/junior/data-structures.yaml @@ -76,11 +76,7 @@ questions: text: en: "How does list slicing work in Python? What are common list methods for manipulation?" ru: "Как работают срезы (slicing) списков в Python? Какие есть распространённые методы для работы со списками?" - code: | - nums = [0, 1, 2, 3, 4, 5] - nums[1:4] # ??? - nums[::-1] # ??? - nums[::2] # ??? + code: null follow_ups: en: - "What is the time complexity of list.append() vs list.insert()?" @@ -224,17 +220,6 @@ questions: ru: "Как устроены словари (dict) и какие объекты могут служить ключами? Что представляет собой хэш-таблица и возможно ли, чтобы разные объекты имели одинаковый хэш?" code: null follow_ups: - en: - - "Why must dictionary keys be hashable?" - - "What happens when two keys have the same hash (collision)?" - - "How does Python handle hash collisions in dictionaries?" - - "Can a list be used as a dictionary key? Why or why not?" - ru: - - "Почему ключи словаря должны быть хешируемыми?" - - "Что происходит, когда два ключа имеют одинаковый хэш (коллизия)?" - - "Как Python обрабатывает коллизии хэшей в словарях?" - - "Можно ли использовать список как ключ словаря? Почему да или нет?" - expected_points: - "Dictionaries are implemented as hash tables (open addressing with probing)" - "Keys must be hashable (have __hash__ method) and immutable (or effectively immutable)" - "Hash collisions are possible; Python handles them with probing (linear or quadratic)" diff --git a/data/questions/python/junior/django-drf.yaml b/data/questions/python/junior/django-drf.yaml index 9d2eb57..935cea1 100644 --- a/data/questions/python/junior/django-drf.yaml +++ b/data/questions/python/junior/django-drf.yaml @@ -13,12 +13,7 @@ questions: text: en: "What is a Serializer in Django REST Framework? How does it differ from a Django Form?" ru: "Что такое Serializer в Django REST Framework? Чем он отличается от Django Form?" - code: | - from rest_framework import serializers - - class ArticleSerializer(serializers.Serializer): - title = serializers.CharField(max_length=200) - body = serializers.CharField() + code: null follow_ups: en: - "What methods validate and convert data to Python objects?" @@ -39,11 +34,7 @@ questions: text: en: "What is ModelSerializer? How does it relate to Django models?" ru: "Что такое ModelSerializer? Как он связан с моделями Django?" - code: | - class ArticleSerializer(serializers.ModelSerializer): - class Meta: - model = Article - fields = ["id", "title", "published"] + code: null follow_ups: en: - "How do you exclude sensitive fields from serialization?" @@ -64,16 +55,7 @@ questions: text: en: "What is APIView in DRF? How do you handle GET and POST in one class?" ru: "Что такое APIView в DRF? Как обработать GET и POST в одном классе?" - code: | - from rest_framework.views import APIView - from rest_framework.response import Response - - class ArticleList(APIView): - def get(self, request): - return Response([]) - - def post(self, request): - return Response({"created": True}, status=201) + code: null follow_ups: en: - "How does APIView differ from Django's View?" @@ -94,12 +76,7 @@ questions: text: en: "What is a ViewSet and DefaultRouter in DRF? What endpoints do they generate?" ru: "Что такое ViewSet и DefaultRouter в DRF? Какие эндпоинты они создают?" - code: | - from rest_framework import viewsets - from rest_framework.routers import DefaultRouter - - router = DefaultRouter() - router.register(r"articles", ArticleViewSet) + code: null follow_ups: en: - "What is the difference between ModelViewSet and ViewSet?" @@ -164,12 +141,7 @@ questions: text: en: "What are generic views (ListAPIView, CreateAPIView) in DRF? When are they useful?" ru: "Что такое generic views (ListAPIView, CreateAPIView) в DRF? Когда они полезны?" - code: | - from rest_framework.generics import ListCreateAPIView - - class ArticleListCreate(ListCreateAPIView): - queryset = Article.objects.all() - serializer_class = ArticleSerializer + code: null follow_ups: en: - "How do generic views reduce boilerplate vs APIView?" diff --git a/data/questions/python/junior/django.yaml b/data/questions/python/junior/django.yaml index 454d78d..efb8d7e 100644 --- a/data/questions/python/junior/django.yaml +++ b/data/questions/python/junior/django.yaml @@ -35,12 +35,7 @@ questions: text: en: "How do you define a Django model? What is a migration and why is it needed?" ru: "Как объявить модель Django? Что такое миграция и зачем она нужна?" - code: | - from django.db import models - - class Article(models.Model): - title = models.CharField(max_length=200) - published = models.DateTimeField(auto_now_add=True) + code: null follow_ups: en: - "What commands create and apply migrations?" @@ -61,10 +56,7 @@ questions: text: en: "What is a QuerySet in Django ORM? Explain filter(), get(), and all()." ru: "Что такое QuerySet в Django ORM? Объясните filter(), get() и all()." - code: | - Article.objects.filter(published__year=2024) - Article.objects.get(pk=1) - Article.objects.all() + code: null follow_ups: en: - "What exception does get() raise when no row matches?" @@ -85,13 +77,7 @@ questions: text: en: "What is Django Admin? How do you register a model in the admin site?" ru: "Что такое Django Admin? Как зарегистрировать модель в админке?" - code: | - from django.contrib import admin - from .models import Article - - @admin.register(Article) - class ArticleAdmin(admin.ModelAdmin): - list_display = ("title", "published") + code: null follow_ups: en: - "How do you create a superuser for admin access?" @@ -112,14 +98,7 @@ questions: text: en: "How does URL routing work in Django? What is the difference between FBV and CBV?" ru: "Как работает маршрутизация URL в Django? В чём разница между FBV и CBV?" - code: | - # urls.py - from django.urls import path - from . import views - - urlpatterns = [ - path("articles/", views.article_list, name="article-list"), - ] + code: null follow_ups: en: - "What is include() used for in urlpatterns?" diff --git a/data/questions/python/junior/exceptions.yaml b/data/questions/python/junior/exceptions.yaml index 2768c9a..ab65448 100644 --- a/data/questions/python/junior/exceptions.yaml +++ b/data/questions/python/junior/exceptions.yaml @@ -13,17 +13,7 @@ questions: text: en: "Explain the try/except/else/finally blocks in Python. What is the purpose of each?" ru: "Объясните блоки try/except/else/finally в Python. Каково назначение каждого из них?" - code: | - try: - result = risky_operation() - except ValueError as e: - print(f"Value error: {e}") - except (TypeError, RuntimeError) as e: - print(f"Type or runtime error: {e}") - else: - print(f"Success: {result}") - finally: - cleanup() + code: null follow_ups: en: - "When does the 'else' block execute?" @@ -71,14 +61,7 @@ questions: text: en: "How do you raise and create custom exceptions in Python?" ru: "Как создавать и выбрасывать пользовательские исключения в Python?" - code: | - class ValidationError(Exception): - """Raised when validation fails.""" - pass - - def validate_age(age: int): - if age < 0: - raise ValidationError("Age cannot be negative") + code: null follow_ups: en: - "How do you chain exceptions with 'raise from'?" diff --git a/data/questions/python/junior/fastapi.yaml b/data/questions/python/junior/fastapi.yaml index be95c39..f3a3725 100644 --- a/data/questions/python/junior/fastapi.yaml +++ b/data/questions/python/junior/fastapi.yaml @@ -34,14 +34,7 @@ questions: text: en: "How do you define HTTP routes in FastAPI? Explain path parameters vs query parameters." ru: "Как объявить HTTP-маршруты в FastAPI? Объясните разницу между path- и query-параметрами." - code: | - from fastapi import FastAPI - - app = FastAPI() - - @app.get("/items/{item_id}") - async def read_item(item_id: int, q: str | None = None): - return {"item_id": item_id, "q": q} + code: null follow_ups: en: - "How does FastAPI validate that item_id is an integer?" @@ -62,12 +55,7 @@ questions: text: en: "How does Pydantic validate request bodies in FastAPI? What happens on validation failure?" ru: "Как Pydantic валидирует тело запроса в FastAPI? Что происходит при ошибке валидации?" - code: | - from pydantic import BaseModel, Field - - class ItemCreate(BaseModel): - name: str = Field(min_length=1, max_length=100) - price: float = Field(gt=0) + code: null follow_ups: en: - "What HTTP status code does FastAPI return for invalid body?" @@ -88,17 +76,7 @@ questions: text: en: "What is FastAPI's Depends() used for? Give a simple example of dependency injection." ru: "Для чего используется Depends() в FastAPI? Приведите простой пример внедрения зависимостей." - code: | - from fastapi import Depends, FastAPI - - app = FastAPI() - - def get_pagination(skip: int = 0, limit: int = 10): - return {"skip": skip, "limit": limit} - - @app.get("/items") - async def list_items(pagination=Depends(get_pagination)): - return pagination + code: null follow_ups: en: - "Can dependencies depend on other dependencies?" @@ -140,15 +118,7 @@ questions: text: en: "How do you test a FastAPI endpoint without running a real server? What is TestClient?" ru: "Как протестировать эндпоинт FastAPI без запуска реального сервера? Что такое TestClient?" - code: | - from fastapi.testclient import TestClient - from main import app - - client = TestClient(app) - - def test_read_root(): - response = client.get("/") - assert response.status_code == 200 + code: null follow_ups: en: - "Does TestClient work with async endpoints?" @@ -169,14 +139,7 @@ questions: text: en: "How do you return custom HTTP status codes and error responses in FastAPI?" ru: "Как вернуть произвольные HTTP-коды и ответы об ошибках в FastAPI?" - code: | - from fastapi import HTTPException - - @app.get("/items/{item_id}") - async def read_item(item_id: int): - if item_id < 1: - raise HTTPException(status_code=404, detail="Item not found") - return {"item_id": item_id} + code: null follow_ups: en: - "What is the difference between raising HTTPException and returning a Response?" diff --git a/data/questions/python/junior/functions.yaml b/data/questions/python/junior/functions.yaml index c3a67d1..97fd927 100644 --- a/data/questions/python/junior/functions.yaml +++ b/data/questions/python/junior/functions.yaml @@ -36,9 +36,7 @@ questions: text: en: "What are lambda functions in Python? When should you use them vs regular named functions?" ru: "Что такое лямбда-функции в Python? Когда их использовать вместо обычных именованных функций?" - code: | - square = lambda x: x ** 2 - squares = list(map(lambda x: x ** 2, [1, 2, 3])) + code: null follow_ups: en: - "What are the limitations of lambda functions?" @@ -60,15 +58,7 @@ questions: text: en: "Explain Python's LEGB scoping rule. How does it affect variable lookup?" ru: "Объясните правило областей видимости LEGB в Python. Как оно влияет на поиск переменных?" - code: | - x = "global" - def outer(): - x = "enclosing" - def inner(): - x = "local" - print(x) - inner() - outer() + code: null follow_ups: en: - "What do 'global' and 'nonlocal' keywords do?" @@ -90,13 +80,7 @@ questions: text: en: "What is the problem with mutable default arguments in Python? Why can it lead to bugs?" ru: "В чём проблема изменяемых аргументов по умолчанию в Python? Почему это может приводить к ошибкам?" - code: | - def add_item(item, items=[]): - items.append(item) - return items - - print(add_item(1)) # [1] - print(add_item(2)) # [1, 2] — unexpected! + code: null follow_ups: en: - "How can you safely use mutable defaults?" @@ -139,19 +123,7 @@ questions: text: en: "What does it mean that functions are first-class citizens in Python? Give examples." ru: "Что означает, что функции в Python являются объектами первого класса? Приведите примеры." - code: | - def greet(name): - return f"Hello, {name}!" - - # Assign to variable - say_hello = greet - print(say_hello("World")) - - # Pass as argument - def apply(func, value): - return func(value) - - print(apply(greet, "Python")) + code: null follow_ups: en: - "What is a higher-order function?" diff --git a/data/questions/python/junior/kafka.yaml b/data/questions/python/junior/kafka.yaml index 3f33c81..04362aa 100644 --- a/data/questions/python/junior/kafka.yaml +++ b/data/questions/python/junior/kafka.yaml @@ -19,13 +19,7 @@ questions: cons, performance, use cases). ru: Какие основные Python-библиотеки для Kafka? Сравните kafka-python, confluent-kafka-python и aiokafka (плюсы, минусы, производительность, сценарии). - code: '# pip install kafka-python (pure Python, no C deps) - - # pip install confluent-kafka (librdkafka required) - - # pip install aiokafka (async wrapper, often based on kafka-python) - - ' + code: null follow_ups: en: - Which library is production-ready? @@ -48,15 +42,11 @@ questions: - serialization question: text: - en: Write a Kafka producer in Python using confluent-kafka-python with JSON serialization, error handling, and callback. - How to send messages asynchronously? - ru: Напишите Kafka продюсера на Python с использованием confluent-kafka-python, с JSON-сериализацией, обработкой ошибок - и callback. Как отправлять сообщения асинхронно? - code: "from confluent_kafka import Producer\nimport json\n\ndef delivery_report(err, msg):\n if err: print(f\"Failed:\ - \ {err}\")\n else: print(f\"Delivered to {msg.topic()}[{msg.partition()}] @ {msg.offset()}\")\n\nconf = {'bootstrap.servers':\ - \ 'localhost:9092', 'client.id': 'py-producer'}\nproducer = Producer(conf)\n\ndata = {'user': 'alice', 'action': 'login'}\n\ - producer.produce('events', key='alice', value=json.dumps(data), callback=delivery_report)\nproducer.flush() # wait\ - \ for all messages\n\n# Async (non-blocking but still need poll)\nproducer.poll(0) # trigger callbacks\n" + en: What is the structure of a Kafka producer in confluent-kafka-python? Explain how JSON serialization, + delivery callbacks, and async sending work. What is the purpose of poll() and flush()? + ru: Как устроен Kafka продюсер в confluent-kafka-python? Объясните, как работают JSON-сериализация, + callback доставки и асинхронная отправка. Для чего нужны poll() и flush()? + code: null follow_ups: en: - What is the purpose of flush() and poll()? diff --git a/data/questions/python/junior/oop.yaml b/data/questions/python/junior/oop.yaml index cda1e39..fc5a37d 100644 --- a/data/questions/python/junior/oop.yaml +++ b/data/questions/python/junior/oop.yaml @@ -35,23 +35,7 @@ questions: text: en: "How does inheritance work in Python? What is Method Resolution Order (MRO)?" ru: "Как работает наследование в Python? Что такое порядок разрешения методов (MRO)?" - code: | - class A: - def method(self): - return "A" - - class B(A): - def method(self): - return "B" - - class C(A): - def method(self): - return "C" - - class D(B, C): - pass - - print(D().method()) # ??? + code: null follow_ups: en: - "How does super() work in multiple inheritance?" @@ -73,12 +57,7 @@ questions: text: en: "How does Python handle visibility and encapsulation? Explain single underscore, double underscore, and name mangling." ru: "Как Python обрабатывает видимость и инкапсуляцию? Объясните одинарное подчёркивание, двойное подчёркивание и name mangling." - code: | - class MyClass: - def __init__(self): - self.public = 1 - self._protected = 2 - self.__private = 3 + code: null follow_ups: en: - "Are private attributes truly private in Python?" @@ -100,18 +79,7 @@ questions: text: en: "How do you create getters and setters in Python? Explain the @property decorator." ru: "Как создавать геттеры и сеттеры в Python? Объясните декоратор @property." - code: | - class Temperature: - def __init__(self, celsius): - self._celsius = celsius - - @property - def fahrenheit(self): - return self._celsius * 9/5 + 32 - - @fahrenheit.setter - def fahrenheit(self, value): - self._celsius = (value - 32) * 5/9 + code: null follow_ups: en: - "When would you use @property instead of a regular attribute?" @@ -133,13 +101,7 @@ questions: text: en: "What are dataclasses in Python? How do they differ from regular classes and __slots__?" ru: "Что такое dataclasses в Python? Чем они отличаются от обычных классов и __slots__?" - code: | - from dataclasses import dataclass - - @dataclass - class Point: - x: float - y: float + code: null follow_ups: en: - "What does frozen=True do in a dataclass?" @@ -185,20 +147,7 @@ questions: text: en: "How do you create abstract classes in Python? What is the ABC module?" ru: "Как создавать абстрактные классы в Python? Что такое модуль ABC?" - code: | - from abc import ABC, abstractmethod - - class Shape(ABC): - @abstractmethod - def area(self) -> float: - pass - - class Circle(Shape): - def __init__(self, radius): - self.radius = radius - - def area(self): - return 3.14 * self.radius ** 2 + code: null follow_ups: en: - "Can you instantiate an abstract class?" diff --git a/data/questions/python/junior/pytest.yaml b/data/questions/python/junior/pytest.yaml index cb94ae1..80b17cf 100644 --- a/data/questions/python/junior/pytest.yaml +++ b/data/questions/python/junior/pytest.yaml @@ -55,12 +55,7 @@ questions: text: en: "How does pytest improve plain assert statements? What happens on failure?" ru: "Как pytest улучшает обычные assert? Что происходит при падении теста?" - code: | - def add(a, b): - return a + b - - def test_add(): - assert add(2, 3) == 5 + code: null follow_ups: en: - "What is assertion introspection?" @@ -81,15 +76,7 @@ questions: text: en: "What is a pytest fixture? How do you declare and use one?" ru: "Что такое фикстура pytest? Как её объявить и использовать?" - code: | - import pytest - - @pytest.fixture - def sample_user(): - return {"id": 1, "name": "Alice"} - - def test_user_name(sample_user): - assert sample_user["name"] == "Alice" + code: null follow_ups: en: - "What is the default scope of a fixture?" @@ -110,12 +97,7 @@ questions: text: en: "What does @pytest.mark.parametrize do? Give an example testing multiple inputs." ru: "Что делает @pytest.mark.parametrize? Приведите пример проверки нескольких входов." - code: | - import pytest - - @pytest.mark.parametrize("a,b,expected", [(1, 2, 3), (0, 0, 0), (-1, 1, 0)]) - def test_add(a, b, expected): - assert a + b == expected + code: null follow_ups: en: - "How many test cases does parametrize generate?" @@ -157,15 +139,7 @@ questions: text: en: "How do you test that code raises an expected exception in pytest?" ru: "Как в pytest проверить, что код выбрасывает ожидаемое исключение?" - code: | - import pytest - - def divide(a, b): - return a / b - - def test_divide_by_zero(): - with pytest.raises(ZeroDivisionError): - divide(1, 0) + code: null follow_ups: en: - "How do you match the exception message with pytest.raises?" diff --git a/data/questions/python/junior/rabbitmq.yaml b/data/questions/python/junior/rabbitmq.yaml index d4d0283..987e795 100644 --- a/data/questions/python/junior/rabbitmq.yaml +++ b/data/questions/python/junior/rabbitmq.yaml @@ -18,29 +18,7 @@ questions: for sync, async, and production use? ru: Какие основные Python клиентские библиотеки для RabbitMQ? Сравните pika, aio-pika и kombu. Какая подходит для синхронного, асинхронного использования и продакшена? - code: '# Pika (synchronous, but can do async via SelectConnection/Asyncore) - - import pika - - connection = pika.BlockingConnection(pika.ConnectionParameters(''localhost'')) - - channel = connection.channel() - - - # Aio-pika (asyncio native) - - import aio_pika - - connection = await aio_pika.connect_robust("amqp://guest:guest@localhost/") - - - # Kombu (abstraction over multiple brokers, used by Celery) - - from kombu import Connection, Exchange, Queue - - conn = Connection(''amqp://guest:guest@localhost:5672//'') - - ' + code: null follow_ups: en: - Is pika production-ready? @@ -66,18 +44,11 @@ questions: - confirms question: text: - en: Write a RabbitMQ producer in Python using pika with mandatory flag and publisher confirms (asynchronous). Show how - to handle confirmation and return callbacks. Include message persistence and exchange declaration. - ru: Напишите RabbitMQ продюсера на Python с использованием pika с флагом mandatory и подтверждениями издателя (асинхронно). - Покажите обработку обратных вызовов подтверждения и возврата. Включите персистентность сообщения и объявление обменника. - code: "import pika\nfrom pika.spec import BasicProperties\n\nconnection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))\n\ - channel = connection.channel()\nchannel.confirm_delivery() # enable publisher confirms\n\n# Callback for successful\ - \ confirm\ndef on_confirm(frame):\n print(f\"Message confirmed: {frame.method.delivery_tag}\")\n\n# Callback for\ - \ mandatory flag (message returned)\ndef on_return(ch, method, properties, body):\n print(f\"Message returned: {body}\"\ - )\n\nchannel.add_on_return_callback(on_return)\nchannel.add_on_delivery_confirm_callback(on_confirm)\n\nchannel.exchange_declare(exchange='my_exchange',\ - \ exchange_type='topic', durable=True)\n\nprops = BasicProperties(delivery_mode=2, content_type='application/json')\ - \ # persistent\nchannel.basic_publish(\n exchange='my_exchange', routing_key='key', body=b'Hello',\n mandatory=True,\ - \ properties=props\n)\nconnection.close()\n" + en: What is the structure of a RabbitMQ producer in pika? Explain how publisher confirms, the mandatory flag, + message persistence, and exchange declaration work together. + ru: Как устроен RabbitMQ продюсер в pika? Объясните, как работают подтверждения издателя (publisher confirms), + флаг mandatory, персистентность сообщений и объявление обменника. + code: null follow_ups: en: - What happens if confirm is not called? diff --git a/data/questions/python/junior/strings.yaml b/data/questions/python/junior/strings.yaml index aba340d..31f5fd3 100644 --- a/data/questions/python/junior/strings.yaml +++ b/data/questions/python/junior/strings.yaml @@ -13,12 +13,7 @@ questions: text: en: "What are the different ways to format strings in Python? Compare f-strings, .format(), and %-formatting." ru: "Какие существуют способы форматирования строк в Python? Сравните f-строки, .format() и %-форматирование." - code: | - name, age = "Alice", 30 - # Compare these approaches - f"Hello, {name} is {age}" - "{} is {}".format(name, age) - "%s is %d" % (name, age) + code: null follow_ups: en: - "Why are f-strings preferred in modern Python?" @@ -40,9 +35,7 @@ questions: text: en: "Are strings mutable or immutable in Python? How does this affect common string operations?" ru: "Строки изменяемы или неизменяемы в Python? Как это влияет на обычные операции со строками?" - code: | - s = "hello" - s_upper = s.upper() # What does s contain after this? + code: null follow_ups: en: - "If strings are immutable, how does s += ' world' work?" @@ -63,13 +56,7 @@ questions: text: en: "How does string slicing work in Python? Explain negative indexing and step values." ru: "Как работают срезы строк в Python? Объясните отрицательную индексацию и значения шага." - code: | - s = "Python" - # What do these produce? - s[0:3] # ??? - s[-3:] # ??? - s[::-1] # ??? - s[::2] # ??? + code: null follow_ups: en: - "What is the difference between s[0] and s[0:1]?" @@ -116,9 +103,7 @@ questions: text: en: "What are raw strings in Python? When would you use them?" ru: "Что такое сырые строки (raw strings) в Python? Когда их использовать?" - code: | - normal = "C:\Users\name" - raw = r"C:\Users\name" + code: null follow_ups: en: - "How do raw strings help with regular expressions?" diff --git a/data/questions/python/middle/architecture.yaml b/data/questions/python/middle/architecture.yaml index 6cdd3dd..ef50d8b 100644 --- a/data/questions/python/middle/architecture.yaml +++ b/data/questions/python/middle/architecture.yaml @@ -62,16 +62,7 @@ questions: text: en: "What is dependency injection? How do you implement it in Python without a framework?" ru: "Что такое внедрение зависимостей (dependency injection)? Как реализовать его в Python без фреймворка?" - code: | - # Tight coupling - class UserService: - def __init__(self): - self.repo = PostgresUserRepo() - - # With dependency injection - class UserService: - def __init__(self, repo: UserRepository): - self.repo = repo + code: null follow_ups: en: - "What are the benefits and drawbacks of DI frameworks?" diff --git a/data/questions/python/middle/asyncio.yaml b/data/questions/python/middle/asyncio.yaml index e40b08d..c313f0b 100644 --- a/data/questions/python/middle/asyncio.yaml +++ b/data/questions/python/middle/asyncio.yaml @@ -13,10 +13,7 @@ questions: text: en: "What is asyncio.TaskGroup (Python 3.11+)? How does it improve structured concurrency?" ru: "Что такое asyncio.TaskGroup (Python 3.11+)? Как он улучшает structured concurrency?" - code: | - async with asyncio.TaskGroup() as tg: - tg.create_task(fetch(url1)) - tg.create_task(fetch(url2)) + code: null follow_ups: en: - "What happens if one task in TaskGroup fails?" @@ -37,15 +34,7 @@ questions: text: en: "How does asyncio.Queue work? Give a producer-consumer example." ru: "Как работает asyncio.Queue? Приведите пример producer-consumer." - code: | - queue = asyncio.Queue(maxsize=10) - - async def producer(): - await queue.put(item) - - async def consumer(): - item = await queue.get() - queue.task_done() + code: null follow_ups: en: - "What is the difference between Queue and PriorityQueue?" @@ -66,15 +55,7 @@ questions: text: en: "What synchronization primitives does asyncio provide? When use Lock vs Semaphore?" ru: "Какие примитивы синхронизации есть в asyncio? Когда Lock, а когда Semaphore?" - code: | - lock = asyncio.Lock() - sem = asyncio.Semaphore(5) - - async with lock: - ... - - async with sem: - ... + code: null follow_ups: en: - "Why must you not use threading.Lock in async code?" @@ -95,9 +76,7 @@ questions: text: en: "What is loop.run_in_executor()? When must you use it?" ru: "Что такое loop.run_in_executor()? Когда его нужно использовать?" - code: | - loop = asyncio.get_running_loop() - result = await loop.run_in_executor(None, blocking_io, arg) + code: null follow_ups: en: - "What executor is used when None is passed?" @@ -118,13 +97,7 @@ questions: text: en: "How does task cancellation work in asyncio? What is asyncio.shield()?" ru: "Как работает отмена задач в asyncio? Что такое asyncio.shield()?" - code: | - task = asyncio.create_task(long_running()) - task.cancel() - try: - await task - except asyncio.CancelledError: - pass + code: null follow_ups: en: - "What should cleanup code do on CancelledError?" diff --git a/data/questions/python/middle/django-drf.yaml b/data/questions/python/middle/django-drf.yaml index dc68621..7144948 100644 --- a/data/questions/python/middle/django-drf.yaml +++ b/data/questions/python/middle/django-drf.yaml @@ -13,11 +13,7 @@ questions: text: en: "How do permission classes work in DRF? Compare IsAuthenticated and DjangoModelPermissions." ru: "Как работают permission classes в DRF? Сравните IsAuthenticated и DjangoModelPermissions." - code: | - from rest_framework.permissions import IsAuthenticated - - class ArticleViewSet(viewsets.ModelViewSet): - permission_classes = [IsAuthenticated] + code: null follow_ups: en: - "How do you implement object-level permissions?" @@ -38,13 +34,7 @@ questions: text: en: "What is throttling in DRF? How do you rate-limit API clients?" ru: "Что такое throttling в DRF? Как ограничить частоту запросов клиентов API?" - code: | - REST_FRAMEWORK = { - "DEFAULT_THROTTLE_CLASSES": [ - "rest_framework.throttling.UserRateThrottle", - ], - "DEFAULT_THROTTLE_RATES": {"user": "100/hour"}, - } + code: null follow_ups: en: - "What HTTP status is returned when throttled?" @@ -65,14 +55,7 @@ questions: text: en: "How do nested serializers represent related objects in DRF?" ru: "Как nested serializers представляют связанные объекты в DRF?" - code: | - class CommentSerializer(serializers.ModelSerializer): - class Meta: - model = Comment - fields = ["id", "text"] - - class ArticleSerializer(serializers.ModelSerializer): - comments = CommentSerializer(many=True, read_only=True) + code: null follow_ups: en: - "What is the writable nested serializer challenge?" @@ -93,12 +76,7 @@ questions: text: en: "How do you filter querysets in DRF? What does django-filter integration provide?" ru: "Как фильтровать queryset в DRF? Что даёт интеграция django-filter?" - code: | - from django_filters.rest_framework import DjangoFilterBackend - - class ArticleViewSet(viewsets.ModelViewSet): - filter_backends = [DjangoFilterBackend] - filterset_fields = ["status", "author"] + code: null follow_ups: en: - "What is SearchFilter used for?" diff --git a/data/questions/python/middle/django.yaml b/data/questions/python/middle/django.yaml index fa17b0f..db5e439 100644 --- a/data/questions/python/middle/django.yaml +++ b/data/questions/python/middle/django.yaml @@ -13,9 +13,7 @@ questions: text: en: "What is the N+1 query problem in Django? How do select_related and prefetch_related help?" ru: "Что такое проблема N+1 запросов в Django? Как помогают select_related и prefetch_related?" - code: | - Article.objects.select_related("author").all() - Article.objects.prefetch_related("tags").all() + code: null follow_ups: en: - "When use select_related vs prefetch_related?" @@ -36,13 +34,7 @@ questions: text: en: "How do custom managers and QuerySets work in Django?" ru: "Как работают кастомные managers и QuerySets в Django?" - code: | - class PublishedQuerySet(models.QuerySet): - def published(self): - return self.filter(status="published") - - class Article(models.Model): - objects = PublishedQuerySet.as_manager() + code: null follow_ups: en: - "What is the difference between Manager and QuerySet methods?" @@ -63,14 +55,7 @@ questions: text: en: "What are Django signals? When should you use them and when avoid them?" ru: "Что такое сигналы Django? Когда их использовать, а когда избегать?" - code: | - from django.db.models.signals import post_save - from django.dispatch import receiver - - @receiver(post_save, sender=User) - def create_profile(sender, instance, created, **kwargs): - if created: - Profile.objects.create(user=instance) + code: null follow_ups: en: - "What is the difference between pre_save and post_save?" @@ -91,15 +76,7 @@ questions: text: en: "How does Django's caching framework work? Compare per-view vs low-level cache API." ru: "Как работает кеширование в Django? Сравните per-view и low-level cache API." - code: | - from django.views.decorators.cache import cache_page - from django.core.cache import cache - - @cache_page(60 * 15) - def my_view(request): - ... - - cache.set("key", value, timeout=300) + code: null follow_ups: en: - "What backends can Django cache use (Redis, Memcached)?" diff --git a/data/questions/python/middle/fastapi.yaml b/data/questions/python/middle/fastapi.yaml index 4a7ca83..29d72db 100644 --- a/data/questions/python/middle/fastapi.yaml +++ b/data/questions/python/middle/fastapi.yaml @@ -13,13 +13,7 @@ questions: text: en: "How do FastAPI dependencies with yield work? Why are they useful for database sessions?" ru: "Как работают зависимости FastAPI с yield? Зачем они нужны для сессий БД?" - code: | - async def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() + code: null follow_ups: en: - "What runs after the response is sent?" @@ -40,14 +34,7 @@ questions: text: en: "What is ASGI middleware in FastAPI? How do you configure CORS?" ru: "Что такое ASGI middleware в FastAPI? Как настроить CORS?" - code: | - from fastapi.middleware.cors import CORSMiddleware - - app.add_middleware( - CORSMiddleware, - allow_origins=["https://example.com"], - allow_methods=["*"], - ) + code: null follow_ups: en: - "In what order does middleware wrap the application?" @@ -68,13 +55,7 @@ questions: text: en: "How do you register global exception handlers in FastAPI?" ru: "Как зарегистрировать глобальные обработчики исключений в FastAPI?" - code: | - from fastapi import Request - from fastapi.responses import JSONResponse - - @app.exception_handler(ValueError) - async def value_error_handler(request: Request, exc: ValueError): - return JSONResponse(status_code=400, content={"detail": str(exc)}) + code: null follow_ups: en: - "How does this differ from raising HTTPException in each route?" @@ -95,12 +76,7 @@ questions: text: en: "What are BackgroundTasks in FastAPI? When should you use them vs Celery?" ru: "Что такое BackgroundTasks в FastAPI? Когда использовать их вместо Celery?" - code: | - from fastapi import BackgroundTasks - - @app.post("/send-email") - async def send_email(background_tasks: BackgroundTasks): - background_tasks.add_task(send_notification, "user@example.com") + code: null follow_ups: en: - "Do background tasks run after the response is returned?" diff --git a/data/questions/python/middle/kafka.yaml b/data/questions/python/middle/kafka.yaml index 1f295ea..d4a41a6 100644 --- a/data/questions/python/middle/kafka.yaml +++ b/data/questions/python/middle/kafka.yaml @@ -13,16 +13,11 @@ questions: - offset question: text: - en: Implement a Kafka consumer in Python (confluent-kafka) reading messages from a topic with manual offset commit. - Explain auto commit vs manual commit, synchronous vs asynchronous commit. - ru: Реализуйте Kafka консьюмера на Python (confluent-kafka) с чтением сообщений из топика и ручным коммитом офсета. - Объясните авто-коммит vs ручной коммит, синхронный vs асинхронный коммит. - code: "from confluent_kafka import Consumer\n\nconf = {\n 'bootstrap.servers': 'localhost:9092',\n 'group.id': 'mygroup',\n\ - \ 'auto.offset.reset': 'earliest',\n 'enable.auto.commit': False # manual commit\n}\nconsumer = Consumer(conf)\n\ - consumer.subscribe(['mytopic'])\n\ntry:\n while True:\n msg = consumer.poll(1.0)\n if msg is None:\ - \ continue\n if msg.error():\n print(f\"Error: {msg.error()}\")\n continue\n # process\ - \ message\n print(f\"Received: {msg.value()}\")\n \n # commit synchronously\n consumer.commit(asynchronous=False)\n\ - \ # or async: consumer.commit(asynchronous=True)\nexcept KeyboardInterrupt:\n pass\nfinally:\n consumer.close()\n" + en: What is the difference between auto commit and manual offset commit in Kafka consumers? Explain + synchronous vs asynchronous commit, and when to use each approach. + ru: В чём разница между авто-коммитом и ручным коммитом офсетов в Kafka консьюмерах? Объясните + синхронный vs асинхронный коммит. Когда использовать каждый подход? + code: null follow_ups: en: - What happens if commit fails? @@ -49,17 +44,11 @@ questions: - fastapi question: text: - en: How to consume Kafka messages asynchronously using aiokafka inside a FastAPI application? Show proper startup/shutdown, - concurrency control, and backpressure handling. - ru: Как асинхронно потреблять сообщения Kafka с помощью aiokafka внутри FastAPI приложения? Покажите правильный запуск/остановку, - контроль конкурентности и управление backpressure. - code: "from fastapi import FastAPI\nfrom aiokafka import AIOKafkaConsumer\nimport asyncio\n\napp = FastAPI()\nconsumer\ - \ = AIOKafkaConsumer('mytopic', bootstrap_servers='localhost:9092', group_id='fastapi-group')\n\n@app.on_event('startup')\n\ - async def startup():\n await consumer.start()\n asyncio.create_task(consume())\n\n@app.on_event('shutdown')\n\ - async def shutdown():\n await consumer.stop()\n\nasync def consume():\n async for msg in consumer:\n #\ - \ process message (limit concurrency)\n await process_message(msg)\n # commit manually if needed\n \ - \ await consumer.commit()\n\nasync def process_message(msg):\n # simulate processing\n await asyncio.sleep(0.1)\n\ - \ print(f\"Processed: {msg.value}\")\n" + en: How does an async Kafka consumer work with aiokafka inside a FastAPI application? Explain the + startup/shutdown lifecycle, concurrency control, and backpressure handling patterns. + ru: Как работает асинхронный Kafka консьюмер с aiokafka внутри FastAPI приложения? Объясните + жизненный цикл запуска/остановки, контроль конкурентности и управление backpressure. + code: null follow_ups: en: - How to limit max inflight messages (concurrency) with aiokafka? @@ -91,12 +80,7 @@ questions: poison pills, and exponential backoff. How to implement custom retry with commit control? ru: Как обрабатывать ошибки обработки сообщений и повторы в Python Kafka консьюмере? Обсудите паттерн dead-letter queue (DLQ), ядовитые сообщения и экспоненциальную задержку. Как реализовать повтор с контролем коммита? - code: "# Dead-letter topic approach\n# On processing error: send raw message to 'topic-dlq', commit offset of original\ - \ message\n\nfrom confluent_kafka import Consumer, Producer\nimport time\n\nconsumer = Consumer(...)\nproducer = Producer(...)\n\ - \nwhile True:\n msg = consumer.poll(1.0)\n try:\n process(msg)\n consumer.commit()\n except Exception\ - \ as e:\n # send to DLQ\n producer.produce('my-topic-dlq', value=msg.value(), key=msg.key())\n \ - \ producer.flush()\n consumer.commit() # skip this message\n # or implement retry with backoff by not\ - \ committing and seeking\n" + code: null follow_ups: en: - What is the difference between retry and reprocess? @@ -129,12 +113,7 @@ questions: ru: 'Какие метрики и инструменты важны для мониторинга Python Kafka приложений? Как измерить lag консьюмера, пропускную способность продюсера, ошибки. Как отлаживать типичные проблемы: ''message too large'', ''broker not available'', ''rebalance timeout''.' - code: "# Get consumer lag from Kafka consumer group command\n# kafka-consumer-groups --bootstrap-server localhost:9092\ - \ --group mygroup --describe\n\n# In Python, use confluent_kafka's list_consumer_group_lags()\nfrom confluent_kafka.admin\ - \ import AdminClient\nadmin = AdminClient({'bootstrap.servers': 'localhost:9092'})\nlags = admin.list_consumer_group_lags('mygroup')\n\ - for topic, partition, lag in lags:\n print(f\"{topic}-{partition}: lag={lag}\")\n\n# Producer metrics: delivery errors,\ - \ request latency, queue length\n# Use stats callback in confluent_kafka\ndef stats_cb(stats_json):\n stats = json.loads(stats_json)\n\ - \ print(f\"Outgoing queue: {stats['outbuf_cnt']}, message count: {stats['txmsgs']}\")\n" + code: null follow_ups: en: - How to alert when consumer lag exceeds threshold? diff --git a/data/questions/python/middle/metaprogramming.yaml b/data/questions/python/middle/metaprogramming.yaml index 629fe6b..30238d9 100644 --- a/data/questions/python/middle/metaprogramming.yaml +++ b/data/questions/python/middle/metaprogramming.yaml @@ -13,14 +13,7 @@ questions: text: en: "What are metaclasses in Python? How do you create one and when would you use them?" ru: "Что такое метаклассы в Python? Как создать метакласс и когда их использовать?" - code: | - class Meta(type): - def __new__(mcs, name, bases, namespace): - namespace['created_at'] = 'generated' - return super().__new__(mcs, name, bases, namespace) - - class MyClass(metaclass=Meta): - pass + code: null follow_ups: en: - "How does type() work as a metaclass?" @@ -44,21 +37,7 @@ questions: text: en: "What are descriptors in Python? How do __get__, __set__, and __delete__ work?" ru: "Что такое дескрипторы в Python? Как работают __get__, __set__ и __delete__?" - code: | - class ValidatedAttribute: - def __init__(self, validator): - self.validator = validator - self.data = {} - - def __get__(self, obj, objtype=None): - if obj is None: - return self - return self.data.get(id(obj)) - - def __set__(self, obj, value): - if not self.validator(value): - raise ValueError(f"Invalid value: {value}") - self.data[id(obj)] = value + code: null follow_ups: en: - "What is the difference between a data descriptor and non-data descriptor?" @@ -82,23 +61,7 @@ questions: text: en: "What is the difference between a function decorator and a class-based decorator? When would you use each?" ru: "В чём разница между декоратором-функцией и декоратором-классом? Когда использовать каждый из них?" - code: | - # Function decorator - def logged(func): - @functools.wraps(func) - def wrapper(*args, **kwargs): - print(f"Calling {func.__name__}") - return func(*args, **kwargs) - return wrapper - - # Class decorator - class Logged: - def __init__(self, func): - self.func = func - - def __call__(self, *args, **kwargs): - print(f"Calling {self.func.__name__}") - return self.func(*args, **kwargs) + code: null follow_ups: en: - "How do you create a decorator with arguments?" diff --git a/data/questions/python/middle/pytest.yaml b/data/questions/python/middle/pytest.yaml index 4c71b21..1d8ff12 100644 --- a/data/questions/python/middle/pytest.yaml +++ b/data/questions/python/middle/pytest.yaml @@ -13,12 +13,7 @@ questions: text: en: "What fixture scopes exist in pytest? When use session vs function scope?" ru: "Какие scope у фикстур в pytest? Когда использовать session vs function?" - code: | - @pytest.fixture(scope="session") - def db_engine(): - engine = create_engine() - yield engine - engine.dispose() + code: null follow_ups: en: - "What is module scope useful for?" @@ -39,11 +34,7 @@ questions: text: en: "What does autouse=True on a fixture do?" ru: "Что делает autouse=True у фикстуры?" - code: | - @pytest.fixture(autouse=True) - def reset_cache(): - cache.clear() - yield + code: null follow_ups: en: - "When is autouse helpful vs explicit fixture parameters?" @@ -64,10 +55,7 @@ questions: text: en: "What is the monkeypatch fixture in pytest? Give examples of what it can patch." ru: "Что такое фикстура monkeypatch в pytest? Приведите примеры того, что можно патчить." - code: | - def test_env(monkeypatch): - monkeypatch.setenv("API_KEY", "test") - monkeypatch.setattr("myapp.client.timeout", 1) + code: null follow_ups: en: - "How does monkeypatch undo changes after the test?" @@ -88,15 +76,7 @@ questions: text: en: "How do capsys and caplog fixtures help test output and logging?" ru: "Как фикстуры capsys и caplog помогают тестировать вывод и логирование?" - code: | - def test_print(capsys): - print("hello") - captured = capsys.readouterr() - assert "hello" in captured.out - - def test_log(caplog): - logger.info("event") - assert "event" in caplog.text + code: null follow_ups: en: - "What is capfd used for?" @@ -117,13 +97,7 @@ questions: text: en: "How do you test async code with pytest-asyncio?" ru: "Как тестировать async-код с pytest-asyncio?" - code: | - import pytest - - @pytest.mark.asyncio - async def test_fetch(): - result = await fetch_data() - assert result["ok"] + code: null follow_ups: en: - "What asyncio_mode options exist in pytest config?" diff --git a/data/questions/python/middle/rabbitmq.yaml b/data/questions/python/middle/rabbitmq.yaml index 1e3fb5d..12cff0c 100644 --- a/data/questions/python/middle/rabbitmq.yaml +++ b/data/questions/python/middle/rabbitmq.yaml @@ -14,17 +14,11 @@ questions: - ack question: text: - en: Implement a RabbitMQ consumer using pika with manual acknowledgements, QoS prefetch count=1, and basic_cancel. Show - how to handle graceful shutdown and requeue on failure. - ru: Реализуйте RabbitMQ консьюмера на Python с использованием pika с ручными подтверждениями, QoS prefetch count=1 и - basic_cancel. Покажите корректное завершение работы и повторную постановку в очередь при ошибке. - code: "import pika, signal, sys\n\nconnection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))\nchannel\ - \ = connection.channel()\nchannel.basic_qos(prefetch_count=1)\n\ndef callback(ch, method, properties, body):\n try:\n\ - \ print(f\"Processing {body}\")\n # ... processing that may raise\n ch.basic_ack(delivery_tag=method.delivery_tag)\n\ - \ except Exception:\n # requeue = True sends back to same queue (may cause infinite loop)\n ch.basic_nack(delivery_tag=method.delivery_tag,\ - \ requeue=True)\n\ndef signal_handler(sig, frame):\n print(\"Cancelling consumer and closing connection\")\n channel.basic_cancel(consumer_tag='my_tag')\n\ - \ connection.close()\n sys.exit(0)\n\nsignal.signal(signal.SIGINT, signal_handler)\n\nchannel.basic_consume(queue='task_queue',\ - \ on_message_callback=callback, auto_ack=False, consumer_tag='my_tag')\nchannel.start_consuming()\n" + en: What is the difference between basic_ack, basic_nack, and basic_reject in RabbitMQ? Explain + how QoS prefetch count, manual acknowledgements, and graceful shutdown work in a pika consumer. + ru: В чём разница между basic_ack, basic_nack и basic_reject в RabbitMQ? Объясните, как работают + QoS prefetch count, ручные подтверждения и корректное завершение в pika консьюмере. + code: null follow_ups: en: - What is the difference between nack and reject? @@ -50,19 +44,11 @@ questions: - asyncio question: text: - en: Create an async RabbitMQ consumer using aio-pika with robust connection, manual ack, and backpressure handling. - Show integration with FastAPI startup/shutdown events. How to limit concurrent message processing? - ru: Создайте асинхронного RabbitMQ консьюмера с использованием aio-pika с надёжным соединением, ручным ack и управлением - backpressure. Покажите интеграцию с событиями запуска/остановки FastAPI. Как ограничить конкурентную обработку сообщений? - code: "from fastapi import FastAPI\nimport aio_pika\nimport asyncio\n\napp = FastAPI()\nconnection = None\nsemaphore =\ - \ asyncio.Semaphore(10) # limit concurrent processing\n\n@app.on_event(\"startup\")\nasync def startup():\n global\ - \ connection\n connection = await aio_pika.connect_robust(\"amqp://guest:guest@localhost/\")\n channel = await\ - \ connection.channel()\n await channel.set_qos(prefetch_count=20)\n queue = await channel.declare_queue(\"my_queue\"\ - , durable=True)\n await queue.consume(process_message, no_ack=False)\n\n@app.on_event(\"shutdown\")\nasync def shutdown():\n\ - \ await connection.close()\n\nasync def process_message(message: aio_pika.IncomingMessage):\n async with semaphore:\ - \ # backpressure\n async with message.process(requeue=False):\n # business logic\n await\ - \ asyncio.sleep(0.1)\n print(f\"Processed {message.body}\")\n # ack automatically when exiting\ - \ process() context manager\n" + en: How does an async RabbitMQ consumer work with aio-pika? Explain the role of connect_robust, + manual ack, backpressure handling via Semaphore, and FastAPI startup/shutdown integration. + ru: Как работает асинхронный RabbitMQ консьюмер с aio-pika? Объясните роль connect_robust, + ручных подтверждений, управления backpressure через Semaphore и интеграцию с FastAPI. + code: null follow_ups: en: - What does connect_robust do? @@ -94,19 +80,7 @@ questions: ru: Как обрабатывать сбои соединения с RabbitMQ и переподключения в Python? Обсудите настройки heartbeat, стратегии восстановления соединения (экспоненциальная задержка, лимиты повторов) и восстановление каналов. Покажите надёжного издателя с автоматическим переподключением. - code: "import pika, time, logging\n\nclass RobustPublisher:\n def __init__(self, amqp_url, retry_limit=5, retry_delay=2):\n\ - \ self.amqp_url = amqp_url\n self.retry_limit = retry_limit\n self.retry_delay = retry_delay\n\ - \ self.connection = None\n self.channel = None\n self.connect()\n \n def connect(self):\n\ - \ for attempt in range(self.retry_limit):\n try:\n params = pika.URLParameters(self.amqp_url)\n\ - \ params.heartbeat = 60 # seconds\n params.socket_timeout = 30\n self.connection\ - \ = pika.BlockingConnection(params)\n self.channel = self.connection.channel()\n self.channel.confirm_delivery()\n\ - \ logging.info(\"Connected to RabbitMQ\")\n return\n except pika.exceptions.AMQPConnectionError\ - \ as e:\n logging.warning(f\"Connection attempt {attempt+1} failed: {e}\")\n time.sleep(self.retry_delay\ - \ * (2 ** attempt)) # exponential backoff\n raise Exception(\"Could not connect after retries\")\n \n \ - \ def publish(self, exchange, routing_key, body):\n try:\n self.channel.basic_publish(exchange, routing_key,\ - \ body, mandatory=True)\n except (pika.exceptions.ChannelClosed, pika.exceptions.ConnectionClosed) as e:\n \ - \ logging.error(f\"Publish error, reconnecting: {e}\")\n self.connect()\n self.channel.basic_publish(exchange,\ - \ routing_key, body, mandatory=True)\n" + code: null follow_ups: en: - What is the difference between connection and channel recovery? @@ -137,12 +111,7 @@ questions: an example of using JSON with content_type header and custom serializer for complex objects. ru: Как работать с сериализацией сообщений в Python клиентах RabbitMQ? Сравните JSON, MessagePack, Avro и Protobuf. Покажите пример использования JSON с заголовком content_type и собственного сериализатора для сложных объектов. - code: "import json, pickle, msgpack\nfrom pika.spec import BasicProperties\n\n# JSON serialization\ndata = {'user': 'bob',\ - \ 'action': 'login'}\nbody = json.dumps(data).encode()\nprops = BasicProperties(content_type='application/json', content_encoding='utf-8')\n\ - channel.basic_publish('', 'queue', body, properties=props)\n\n# Consumer decodes based on content_type\ndef callback(ch,\ - \ method, props, body):\n if props.content_type == 'application/json':\n data = json.loads(body.decode())\n\ - \ elif props.content_type == 'application/msgpack':\n data = msgpack.unpackb(body)\n # ...\n\n# For schema\ - \ evolution, use Avro with Schema Registry (like confluent)\n# Or Protobuf with .proto definitions\n" + code: null follow_ups: en: - What are the advantages of Protobuf over JSON? diff --git a/data/questions/python/middle/type-hints.yaml b/data/questions/python/middle/type-hints.yaml index c97cb5a..4779016 100644 --- a/data/questions/python/middle/type-hints.yaml +++ b/data/questions/python/middle/type-hints.yaml @@ -13,17 +13,7 @@ questions: text: en: "How do generics work in Python? Explain TypeVar, Generic, and bounded type variables." ru: "Как работают дженерики в Python? Объясните TypeVar, Generic и ограниченные типовые переменные." - code: | - from typing import TypeVar, Generic - - T = TypeVar('T') - - class Stack(Generic[T]): - def push(self, item: T) -> None: ... - def pop(self) -> T: ... - - Number = TypeVar('Number', int, float) - def add(a: Number, b: Number) -> Number: ... + code: null follow_ups: en: - "What is the difference between TypeVar with and without constraints?" @@ -47,20 +37,7 @@ questions: text: en: "What are Protocols in Python? How do they enable structural subtyping?" ru: "Что такое протоколы (Protocols) в Python? Как они реализуют структурную типизацию?" - code: | - from typing import Protocol - - class Drawable(Protocol): - def draw(self) -> None: ... - - def render(obj: Drawable) -> None: - obj.draw() - - class Circle: - def draw(self) -> None: - print("Drawing circle") - - render(Circle()) # OK — structurally matches Drawable + code: null follow_ups: en: - "How does Protocol differ from ABC?" @@ -84,19 +61,7 @@ questions: text: en: "How does @typing.overload work? How does it differ from overloading in other languages?" ru: "Как работает @typing.overload? Чем он отличается от перегрузки в других языках?" - code: | - from typing import overload - - @overload - def process(value: int) -> str: ... - - @overload - def process(value: list[int]) -> list[str]: ... - - def process(value: int | list[int]) -> str | list[str]: - if isinstance(value, list): - return [str(x) for x in value] - return str(value) + code: null follow_ups: en: - "Why doesn't Python have true method overloading?" diff --git a/data/questions/python/senior/django.yaml b/data/questions/python/senior/django.yaml index c568431..0a1b6cd 100644 --- a/data/questions/python/senior/django.yaml +++ b/data/questions/python/senior/django.yaml @@ -76,13 +76,7 @@ questions: text: en: "How does Celery integrate with Django? Describe broker, workers, and result backend." ru: "Как Celery интегрируется с Django? Опишите broker, workers и result backend." - code: | - # tasks.py - from celery import shared_task - - @shared_task - def send_welcome_email(user_id): - ... + code: null follow_ups: en: - "What brokers are commonly used (Redis, RabbitMQ)?" @@ -167,8 +161,7 @@ questions: text: en: "Why define a custom User model at project start? What are the migration implications?" ru: "Зачем определять кастомную User model в начале проекта? Какие последствия для миграций?" - code: | - AUTH_USER_MODEL = "accounts.User" + code: null follow_ups: en: - "What happens if you switch User model late?" diff --git a/data/questions/python/senior/fastapi.yaml b/data/questions/python/senior/fastapi.yaml index f30ca36..d469638 100644 --- a/data/questions/python/senior/fastapi.yaml +++ b/data/questions/python/senior/fastapi.yaml @@ -13,8 +13,7 @@ questions: text: en: "How do you deploy FastAPI in production with Uvicorn and Gunicorn workers?" ru: "Как развернуть FastAPI в продакшене с Uvicorn и Gunicorn workers?" - code: | - gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 --bind 0.0.0.0:8000 + code: null follow_ups: en: - "How do you choose the number of workers?" diff --git a/data/questions/python/senior/kafka.yaml b/data/questions/python/senior/kafka.yaml index 648d058..b202ff3 100644 --- a/data/questions/python/senior/kafka.yaml +++ b/data/questions/python/senior/kafka.yaml @@ -14,18 +14,11 @@ questions: - eos question: text: - en: How to achieve exactly-once semantics in Python with confluent-kafka? Implement idempotent producer and transactional - producer/consumer. What are the configuration requirements? - ru: Как достичь exactly-once семантики в Python с confluent-kafka? Реализуйте идемпотентный продюсер и транзакционный - продюсер/консьюмер. Какие требования к конфигурации? - code: "from confluent_kafka import Producer, Consumer\n\n# Idempotent producer\nproducer_conf = {\n 'bootstrap.servers':\ - \ 'localhost:9092',\n 'enable.idempotence': True,\n 'acks': 'all',\n 'max.in.flight.requests.per.connection':\ - \ 5 # must be <=5 with idempotence\n}\nproducer = Producer(producer_conf)\n\n# Transactional producer\ntxn_producer_conf\ - \ = {\n 'bootstrap.servers': 'localhost:9092',\n 'enable.idempotence': True,\n 'transactional.id': 'unique-producer-id'\n\ - }\ntxn_producer = Producer(txn_producer_conf)\ntxn_producer.init_transactions()\ntxn_producer.begin_transaction()\n\ - txn_producer.produce('topic1', b'msg1')\ntxn_producer.produce('topic2', b'msg2')\ntxn_producer.commit_transaction()\n\ - \n# Transactional consumer (isolation.level=read_committed)\nconsumer_conf = {\n 'bootstrap.servers': 'localhost:9092',\n\ - \ 'group.id': 'txn-group',\n 'isolation.level': 'read_committed'\n}\n" + en: How does exactly-once semantics work in Kafka with confluent-kafka-python? Explain the difference + between idempotent producer and transactional producer/consumer. What configuration is required? + ru: Как работает exactly-once семантика в Kafka с confluent-kafka-python? Объясните разницу + между идемпотентным продюсером и транзакционным продюсером/консьюмером. Какая конфигурация требуется? + code: null follow_ups: en: - What is the role of transactional.id? @@ -59,13 +52,7 @@ questions: ru: Как оптимизировать Python Kafka продюсера и консьюмера для максимальной пропускной способности и низкой задержки? Рассмотрите батчинг (batch.size, linger.ms), сжатие (gzip, snappy, lz4, zstd), параметры fetch, и многопоточность vs многопроцессность. - code: "# High-throughput producer config (confluent-kafka)\nproducer_conf = {\n 'bootstrap.servers': 'localhost:9092',\n\ - \ 'batch.size': 32768, # 32KB\n 'linger.ms': 10, # wait up to 10ms to fill batch\n 'compression.type':\ - \ 'lz4', # fastest compression with good ratio\n 'max.in.flight.requests.per.connection': 5,\n 'enable.idempotence':\ - \ False, # disable for max throughput\n}\n\n# High-throughput consumer config\nconsumer_conf = {\n 'bootstrap.servers':\ - \ 'localhost:9092',\n 'fetch.min.bytes': 1024 * 1024, # 1MB\n 'fetch.max.wait.ms': 500,\n 'max.partition.fetch.bytes':\ - \ 10 * 1024 * 1024, # 10MB\n 'auto.commit.interval.ms': 5000,\n}\n\n# Multi-processing consumer\n# Each process\ - \ gets its own consumer with same group.id -> partition assignment\n" + code: null follow_ups: en: - What is the impact of compression on producer CPU and network? @@ -93,21 +80,11 @@ questions: - serialization question: text: - en: How to use Avro serialization with Kafka in Python? Describe integration with Confluent Schema Registry. Show producer - with AvroSerializer and consumer with AvroDeserializer. How to handle schema evolution (backward/forward compatibility)? - ru: Как использовать Avro сериализацию с Kafka в Python? Опишите интеграцию с Confluent Schema Registry. Покажите продюсера - с AvroSerializer и консьюмера с AvroDeserializer. Как обрабатывать эволюцию схемы (обратная/прямая совместимость)? - code: "from confluent_kafka import Producer, Consumer\nfrom confluent_kafka.serialization import SerializationContext,\ - \ MessageField\nfrom confluent_kafka.schema_registry import SchemaRegistryClient\nfrom confluent_kafka.schema_registry.avro\ - \ import AvroSerializer, AvroDeserializer\n\nschema_registry_conf = {'url': 'http://localhost:8081'}\nschema_registry_client\ - \ = SchemaRegistryClient(schema_registry_conf)\n\nvalue_schema_str = \"\"\"\n{\n \"type\": \"record\",\n \"name\"\ - : \"User\",\n \"fields\": [\n {\"name\": \"name\", \"type\": \"string\"},\n {\"name\": \"age\", \"type\": \"\ - int\"}\n ]\n}\n\"\"\"\n\navro_serializer = AvroSerializer(schema_registry_client, value_schema_str)\n\nproducer_conf\ - \ = {'bootstrap.servers': 'localhost:9092'}\nproducer = Producer(producer_conf)\n\nuser = {'name': 'Alice', 'age': 30}\n\ - producer.produce(topic='users', \n value=avro_serializer(user, SerializationContext('users', MessageField.VALUE)))\n\ - producer.flush()\n\navro_deserializer = AvroDeserializer(schema_registry_client)\nconsumer_conf = {'bootstrap.servers':\ - \ 'localhost:9092', 'group.id': 'avro-group'}\nconsumer = Consumer(consumer_conf)\nconsumer.subscribe(['users'])\n\n\ - msg = consumer.poll(1.0)\ndeserialized = avro_deserializer(msg.value(), SerializationContext('users', MessageField.VALUE))\n" + en: How does Avro serialization integrate with Kafka in Python? Explain the role of Confluent Schema Registry, + AvroSerializer, AvroDeserializer, and how schema evolution (backward/forward compatibility) is handled. + ru: Как Avro сериализация интегрируется с Kafka в Python? Объясните роль Confluent Schema Registry, + AvroSerializer, AvroDeserializer и как обрабатывается эволюция схем (обратная/прямая совместимость). + code: null follow_ups: en: - What is the difference between Avro, Protobuf, and JSON Schema? @@ -134,21 +111,13 @@ questions: - high-throughput question: text: - en: 'How to parallelize Kafka message processing in Python using multiprocessing? Show a pattern: one consumer process - that distributes messages to worker processes via queue. Discuss rebalancing, commit coordination, and handling backpressure.' - ru: 'Как распараллелить обработку сообщений Kafka в Python с помощью multiprocessing? Покажите паттерн: один процесс-консьюмер, - который распределяет сообщения в рабочие процессы через очередь. Обсудите ребалансировку, координацию коммитов и управление - backpressure.' - code: "import multiprocessing as mp\nfrom confluent_kafka import Consumer\nimport time\n\ndef worker_process(in_queue,\ - \ worker_id):\n while True:\n msg = in_queue.get()\n if msg is None:\n break\n #\ - \ heavy processing\n time.sleep(0.1)\n print(f\"Worker {worker_id}: processed offset {msg.offset()}\"\ - )\n # signal done? commit requires offset tracking\n\ndef main():\n queue = mp.Queue(maxsize=1000) # backpressure\n\ - \ workers = [mp.Process(target=worker_process, args=(queue, i)) for i in range(4)]\n for w in workers: w.start()\n\ - \ \n consumer = Consumer({'bootstrap.servers': 'localhost:9092', 'group.id': 'mp-group'})\n consumer.subscribe(['topic'])\n\ - \ \n try:\n while True:\n msg = consumer.poll(0.1)\n if msg and not msg.error():\n\ - \ queue.put(msg) # may block if full (backpressure)\n # commit after processing? need\ - \ coordination\n # naive: commit periodically or after queue empty\n finally:\n for _ in workers:\ - \ queue.put(None)\n for w in workers: w.join()\n consumer.close()\n" + en: What patterns exist for parallel Kafka message processing in Python? Discuss the trade-offs between + a single consumer distributing to worker processes via queue vs the process-per-partition pattern. + How do you handle rebalancing, commit coordination, and backpressure? + ru: Какие паттерны существуют для параллельной обработки Kafka сообщений в Python? Обсудите компромиссы между + одним консьюмером, распределяющим сообщения в воркер-процессы через очередь, и паттерном process-per-partition. + Как обрабатывать ребалансировку, координацию коммитов и backpressure? + code: null follow_ups: en: - How to commit offsets reliably with worker processes? diff --git a/data/questions/python/senior/performance.yaml b/data/questions/python/senior/performance.yaml index 0c19fe5..77ee91d 100644 --- a/data/questions/python/senior/performance.yaml +++ b/data/questions/python/senior/performance.yaml @@ -133,12 +133,7 @@ questions: text: en: "How does functools.lru_cache and functools.cache work? When should you use caching to optimize Python code?" ru: "Как работают functools.lru_cache и functools.cache? Когда использовать кеширование для оптимизации Python-кода?" - code: | - from functools import lru_cache - - @lru_cache(maxsize=128) - def expensive_computation(n: int) -> int: - return n ** n + code: null follow_ups: en: - "What are the memory implications of unbounded caching?" diff --git a/data/questions/python/senior/rabbitmq.yaml b/data/questions/python/senior/rabbitmq.yaml index caf47ec..ee8c1f8 100644 --- a/data/questions/python/senior/rabbitmq.yaml +++ b/data/questions/python/senior/rabbitmq.yaml @@ -14,25 +14,11 @@ questions: - async question: text: - en: 'Implement a classic RPC pattern over RabbitMQ in Python: client sends a request with correlation_id and reply_to - queue, server processes and sends response. Show timeout handling and concurrent requests.' - ru: 'Реализуйте классический паттерн RPC поверх RabbitMQ на Python: клиент отправляет запрос с correlation_id и reply_to - очередью, сервер обрабатывает и отправляет ответ. Покажите обработку таймаута и конкурентные запросы.' - code: "# RPC Client (using pika)\nimport pika, uuid, time\n\nclass RpcClient:\n def __init__(self):\n self.connection\ - \ = pika.BlockingConnection()\n self.channel = self.connection.channel()\n result = self.channel.queue_declare(queue='',\ - \ exclusive=True)\n self.callback_queue = result.method.queue\n self.channel.basic_consume(queue=self.callback_queue,\ - \ on_message_callback=self.on_response, auto_ack=True)\n self.response = None\n self.corr_id = None\n\ - \ \n def on_response(self, ch, method, props, body):\n if self.corr_id == props.correlation_id:\n \ - \ self.response = body\n \n def call(self, message, timeout=5):\n self.response = None\n self.corr_id\ - \ = str(uuid.uuid4())\n self.channel.basic_publish(\n exchange='', routing_key='rpc_queue',\n \ - \ properties=pika.BasicProperties(\n reply_to=self.callback_queue,\n correlation_id=self.corr_id,\n\ - \ ),\n body=message\n )\n start = time.time()\n while self.response is None:\n\ - \ self.connection.process_data_events(timeout=0.1)\n if time.time() - start > timeout:\n \ - \ raise TimeoutError()\n return self.response.decode()\n\n# RPC Server\ndef fib(n): return n if n <=\ - \ 1 else fib(n-1) + fib(n-2)\n\ndef on_request(ch, method, props, body):\n n = int(body)\n response = str(fib(n))\n\ - \ ch.basic_publish(\n exchange='', routing_key=props.reply_to,\n properties=pika.BasicProperties(correlation_id=props.correlation_id),\n\ - \ body=response\n )\n ch.basic_ack(delivery_tag=method.delivery_tag)\n\nchannel.basic_qos(prefetch_count=1)\n\ - channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)\n" + en: How does the RPC pattern work over RabbitMQ? Explain the roles of correlation_id, reply_to queue, + timeout handling, and how to handle concurrent requests. Compare exclusive callback queues vs direct-reply-to. + ru: Как работает RPC паттерн поверх RabbitMQ? Объясните роли correlation_id, reply_to очереди, + обработки таймаута и конкурентных запросов. Сравните эксклюзивные callback-очереди и direct-reply-to. + code: null follow_ups: en: - How to make RPC client async with aio-pika? @@ -56,23 +42,13 @@ questions: - delay question: text: - en: How to implement a retry pattern with exponential backoff in RabbitMQ using Python (pika)? Use dead-letter exchange - with delayed retry queue. Show configuration of main queue, retry queue with TTL, and message flow. - ru: Как реализовать паттерн повтора с экспоненциальной задержкой в RabbitMQ на Python (pika)? Используйте dead-letter - exchange с очередью повторных попыток с TTL. Покажите конфигурацию основной очереди, очереди повторов и поток сообщений. - code: "import pika\n\nconn = pika.BlockingConnection()\nch = conn.channel()\n\n# Setup DLX\nch.exchange_declare('dlx_exchange',\ - \ exchange_type='direct', durable=True)\nch.exchange_declare('retry_exchange', exchange_type='direct', durable=True)\n\ - \n# Retry queue with per-message TTL (set by headers or queue arg)\nretry_args = {\n 'x-dead-letter-exchange': 'dlx_exchange',\n\ - \ 'x-dead-letter-routing-key': 'main',\n 'x-message-ttl': 5000 # 5 seconds initial delay\n}\nch.queue_declare('retry_queue',\ - \ durable=True, arguments=retry_args)\nch.queue_bind('retry_queue', 'retry_exchange', routing_key='retry')\n\n# Main\ - \ queue: dead-letter to retry on failure\nmain_args = {\n 'x-dead-letter-exchange': 'retry_exchange',\n 'x-dead-letter-routing-key':\ - \ 'retry'\n}\nch.queue_declare('main_queue', durable=True, arguments=main_args)\nch.queue_bind('main_queue', 'dlx_exchange',\ - \ routing_key='main')\n\n# Consumer on main queue: if fails, nack with requeue=False -> goes to retry via DLX\ndef consumer_callback(ch,\ - \ method, props, body):\n try:\n # process\n ch.basic_ack(method.delivery_tag)\n except Exception:\n\ - \ # optionally increase delay by modifying x-death header? Requires custom header manipulation\n ch.basic_nack(method.delivery_tag,\ - \ requeue=False)\n\n# Retry consumer: consumes from retry_queue and republishes to main (or simply let TTL do the DLX)\n\ - # Actually retry_queue TTL will move message back to main_queue after delay automatically.\n# So we only need to consume\ - \ main_queue.\n\nch.basic_consume('main_queue', consumer_callback, auto_ack=False)\n" + en: How does a retry pattern with dead-letter exchange (DLX) work in RabbitMQ? Explain the configuration + of main queue, retry queue with TTL, and how messages flow between them. What are the limitations + of TTL-based retry and how to implement exponential backoff? + ru: Как работает паттерн повтора с Dead Letter Exchange (DLX) в RabbitMQ? Объясните конфигурацию + основной очереди, очереди повтора с TTL и поток сообщений между ними. Каковы ограничения + TTL-подхода и как реализовать экспоненциальную задержку? + code: null follow_ups: en: - How to increment delay on each retry (exponential backoff) using multiple retry queues? diff --git a/data/questions/questions_map.yaml b/data/questions/questions_map.yaml index d231e51..eddb79b 100644 --- a/data/questions/questions_map.yaml +++ b/data/questions/questions_map.yaml @@ -262,13 +262,13 @@ python: Kafka Client Basics: - What are the main Python libraries for Kafka? Compare kafka-python, confluent-kafka-python, and aiokafka (pros, cons, performance, use cases). - - Write a Kafka producer in Python using confluent-kafka-python with JSON serialization, error handling, and callback. - How to send messages asynchronously? + - What is the structure of a Kafka producer in confluent-kafka-python? Explain JSON serialization, + delivery callbacks, and the purpose of poll() and flush(). RabbitMQ Client Basics: - What are the main Python client libraries for RabbitMQ? Compare pika, aio-pika, and kombu. Which one is suitable for sync, async, and production use? - - Write a RabbitMQ producer in Python using pika with mandatory flag and publisher confirms (asynchronous). Show how to - handle confirmation and return callbacks. Include message persistence and exchange declaration. + - What is the structure of a RabbitMQ producer in pika? Explain publisher confirms, mandatory flag, + message persistence, and exchange declaration. middle: Architecture: - Explain the SOLID principles. How do they apply to Python development? @@ -358,23 +358,23 @@ python: - What are Literal, TypedDict, and Final types in Python? How do they improve type safety? - What tools does typing provide for type narrowing and debugging type hints? Kafka Client Development: - - Implement a Kafka consumer in Python (confluent-kafka) reading messages from a topic with manual offset commit. Explain - auto commit vs manual commit, synchronous vs asynchronous commit. - - How to consume Kafka messages asynchronously using aiokafka inside a FastAPI application? Show proper startup/shutdown, - concurrency control, and backpressure handling. + - Compare auto commit vs manual offset commit in Kafka consumers. Explain synchronous vs asynchronous + commit and when to use each approach. + - How does an async Kafka consumer work with aiokafka inside a FastAPI application? Explain startup/shutdown + lifecycle, concurrency control, and backpressure handling. - How to handle message processing failures and retries in Python Kafka consumer? Discuss dead-letter queue pattern, poison pills, and exponential backoff. How to implement custom retry with commit control? - 'What metrics and tools are essential for monitoring Python Kafka applications? How to measure consumer lag, producer throughput, errors. How to debug common issues: ''message too large'', ''broker not available'', ''rebalance timeout''.' RabbitMQ Client Development: - - Implement a RabbitMQ consumer using pika with manual acknowledgements, QoS prefetch count=1, and basic_cancel. Show - how to handle graceful shutdown and requeue on failure. - - Create an async RabbitMQ consumer using aio-pika with robust connection, manual ack, and backpressure handling. Show - integration with FastAPI startup/shutdown events. How to limit concurrent message processing? + - What is the difference between basic_ack, basic_nack, and basic_reject in RabbitMQ? Explain + how QoS prefetch count, manual acknowledgements, and graceful shutdown work in a pika consumer. + - How does an async RabbitMQ consumer work with aio-pika? Explain the role of connect_robust, + manual ack, backpressure handling via Semaphore, and FastAPI startup/shutdown integration. - How to handle RabbitMQ connection failures and reconnections in Python? Discuss heartbeat settings, connection recovery - strategies (exponential backoff, retry limits), and channel recovery. Show a robust publisher with automatic reconnect. - - How to handle message serialization in RabbitMQ Python clients? Compare JSON, MessagePack, Avro, and Protobuf. Show - an example of using JSON with content_type header and custom serializer for complex objects. + strategies (exponential backoff, retry limits), and channel recovery. + - How to handle message serialization in RabbitMQ Python clients? Compare JSON, MessagePack, Avro, and Protobuf. + How does the content_type header help the consumer choose the correct deserializer? senior: System Design: - How do consensus algorithms like Raft and Paxos work? When would you need them in a Python system? @@ -422,19 +422,19 @@ python: - What are the key security considerations for REST APIs and GraphQL in Python? - What are SSRF, insecure deserialization, and XXE vulnerabilities? How do they apply to Python? Kafka Client Production: - - How to achieve exactly-once semantics in Python with confluent-kafka? Implement idempotent producer and transactional - producer/consumer. What are the configuration requirements? + - How does exactly-once semantics work in Kafka with confluent-kafka-python? Explain the difference between + idempotent producer and transactional producer/consumer. What configuration is required? - How to optimize Python Kafka producer and consumer for maximum throughput and low latency? Discuss batching (batch.size, linger.ms), compression (gzip, snappy, lz4, zstd), fetch parameters, and multi-threading vs multi-processing. - - How to use Avro serialization with Kafka in Python? Describe integration with Confluent Schema Registry. Show producer - with AvroSerializer and consumer with AvroDeserializer. How to handle schema evolution (backward/forward compatibility)? - - 'How to parallelize Kafka message processing in Python using multiprocessing? Show a pattern: one consumer process that - distributes messages to worker processes via queue. Discuss rebalancing, commit coordination, and handling backpressure.' + - How does Avro serialization integrate with Kafka in Python? Explain the role of Confluent Schema Registry, + AvroSerializer, AvroDeserializer, and schema evolution. + - What patterns exist for parallel Kafka message processing in Python? Compare single consumer + worker + processes vs process-per-partition. Discuss rebalancing, commit coordination, and backpressure. RabbitMQ Client Production: - - 'Implement a classic RPC pattern over RabbitMQ in Python: client sends a request with correlation_id and reply_to queue, - server processes and sends response. Show timeout handling and concurrent requests.' - - How to implement a retry pattern with exponential backoff in RabbitMQ using Python (pika)? Use dead-letter exchange - with delayed retry queue. Show configuration of main queue, retry queue with TTL, and message flow. + - How does the RPC pattern work over RabbitMQ? Explain the roles of correlation_id, reply_to queue, + timeout handling, and concurrent requests. + - How does a retry pattern with dead-letter exchange (DLX) work in RabbitMQ? Explain the configuration of + main queue, retry queue with TTL, and the message flow between them. system-design: middle: System Design: diff --git a/data/questions/rabbitmq/junior/fundamentals.yaml b/data/questions/rabbitmq/junior/fundamentals.yaml index 87f816f..82daba0 100644 --- a/data/questions/rabbitmq/junior/fundamentals.yaml +++ b/data/questions/rabbitmq/junior/fundamentals.yaml @@ -17,11 +17,7 @@ questions: from Kafka? ru: Что такое RabbitMQ? Какие протоколы обмена сообщениями поддерживает (AMQP 0-9-1, AMQP 1.0, MQTT, STOMP)? Чем отличается от Kafka? - code: '# AMQP 0-9-1 core components: Exchange, Queue, Binding - - # Producer -> Exchange -> (binding) -> Queue -> Consumer - - ' + code: null follow_ups: en: - When to choose RabbitMQ over Kafka? @@ -49,30 +45,7 @@ questions: of when to use each type.' ru: 'Объясните типы обменников RabbitMQ: direct, topic, fanout, headers. Как работают привязки (bindings) и ключи маршрутизации? Приведите примеры использования каждого типа.' - code: '# Direct exchange: routing key = binding key - - channel.exchange_declare(exchange=''direct_logs'', exchange_type=''direct'') - - channel.queue_bind(queue=''error_queue'', exchange=''direct_logs'', routing_key=''error'') - - - # Topic exchange: wildcards (* for one word, # for zero or more) - - channel.exchange_declare(exchange=''topic_logs'', exchange_type=''topic'') - - channel.queue_bind(queue=''all_queues'', exchange=''topic_logs'', routing_key=''#'') - - - # Fanout: ignores routing key, sends to all bound queues - - channel.exchange_declare(exchange=''broadcast'', exchange_type=''fanout'') - - - # Headers: uses message header attributes instead of routing key - - channel.exchange_declare(exchange=''header_exchange'', exchange_type=''headers'') - - ' + code: null follow_ups: en: - Can multiple bindings exist with different routing keys? @@ -103,13 +76,7 @@ questions: max length, and lazy queues. ru: Какие свойства очередей существуют в RabbitMQ? Объясните durable, exclusive, auto-delete, TTL (для очереди и сообщения), максимальную длину и ленивые очереди (lazy queues). - code: "# Durable queue (survives broker restart)\nchannel.queue_declare(queue='task_queue', durable=True)\n\n# Exclusive\ - \ queue (only for connection, deleted when connection closes)\nchannel.queue_declare(queue='temp', exclusive=True)\n\ - \n# Auto-delete queue (deleted when last consumer unsubscribes)\nchannel.queue_declare(queue='ephemeral', auto_delete=True)\n\ - \n# Per-message TTL (milliseconds)\nchannel.basic_publish(exchange='', routing_key='queue', \n \ - \ properties=pika.BasicProperties(expiration='60000'))\n\n# Queue TTL (auto-delete queue after unused period)\nargs\ - \ = {'x-expires': 1800000} # 30 minutes\nchannel.queue_declare(queue='temp', arguments=args)\n\n# Lazy queue (messages\ - \ stored on disk, not RAM)\nargs = {'x-queue-mode': 'lazy'}\n" + code: null follow_ups: en: - What is the trade-off of lazy queues? diff --git a/data/questions/rabbitmq/middle/messaging.yaml b/data/questions/rabbitmq/middle/messaging.yaml index f2b4af9..048339a 100644 --- a/data/questions/rabbitmq/middle/messaging.yaml +++ b/data/questions/rabbitmq/middle/messaging.yaml @@ -18,12 +18,7 @@ questions: acknowledgements (auto, manual), prefetch count, and message persistence. ru: Какие гарантии доставки предоставляет RabbitMQ? Объясните подтверждения издателя (publisher confirms), флаги mandatory/immediate, подтверждения потребителя (auto, manual), prefetch count и персистентность сообщений. - code: "# Publisher confirms (synchronous wait)\nchannel.confirm_delivery()\nif channel.basic_publish(exchange='', routing_key='queue',\ - \ body='msg', mandatory=True):\n print(\"Confirmed\")\n\n# Asynchronous confirm with callback\nchannel.add_on_return_callback(on_message_returned)\n\ - \n# Consumer manual ack\nchannel.basic_consume(queue='queue', auto_ack=False, on_message_callback=callback)\ndef callback(ch,\ - \ method, properties, body):\n process(body)\n ch.basic_ack(delivery_tag=method.delivery_tag)\n\n# Prefetch count\ - \ (quality of service)\nchannel.basic_qos(prefetch_count=1) # only one unacked message per consumer\n\n# Persistent\ - \ message\nproperties = pika.BasicProperties(delivery_mode=2) # persistent\n" + code: null follow_ups: en: - What is the difference between confirm and transaction? @@ -55,12 +50,7 @@ questions: Show a typical dead-letter queue pattern for failed messages. ru: Что такое Dead Letter Exchange (DLX) в RabbitMQ? Как его настроить для очередей? В каких случаях сообщения попадают в DLX? Покажите типичный паттерн dead-letter очереди для неудачных сообщений. - code: "# Main queue with DLX configuration\nargs = {\n 'x-dead-letter-exchange': 'dlx.exchange',\n 'x-dead-letter-routing-key':\ - \ 'failed',\n 'x-max-retries': 3 # custom argument (needs plugin or app logic)\n}\nchannel.queue_declare(queue='main_queue',\ - \ durable=True, arguments=args)\n\n# Dead-letter exchange and queue\nchannel.exchange_declare('dlx.exchange', exchange_type='direct')\n\ - channel.queue_declare('dlx.queue', durable=True)\nchannel.queue_bind('dlx.queue', 'dlx.exchange', routing_key='failed')\n\ - \n# Message becomes dead-letter when:\n# - rejected (basic.reject/nack) with requeue=false\n# - expired (TTL)\n# - queue\ - \ length exceeded (max-length)\n" + code: null follow_ups: en: - How to implement retry with delay using DLX + TTL? diff --git a/data/questions/rabbitmq/senior/architecture.yaml b/data/questions/rabbitmq/senior/architecture.yaml index a4b6026..dc2e4fb 100644 --- a/data/questions/rabbitmq/senior/architecture.yaml +++ b/data/questions/rabbitmq/senior/architecture.yaml @@ -19,21 +19,7 @@ questions: ru: 'Как работает кластеризация RabbitMQ? Объясните различные стратегии высокой доступности: классические зеркалируемые очереди (HA policy) против quorum queues (на основе Raft). В чём разница в надёжности данных, производительности и поведении при отказе?' - code: '# HA policy for mirrored queues (deprecated but still used) - - rabbitmqctl set_policy ha-all "^ha\." ''{"ha-mode":"all","ha-sync-mode":"automatic"}'' - - - # Quorum queue (recommended for HA) - - args = {''x-queue-type'': ''quorum''} - - channel.queue_declare(queue=''quorum_queue'', durable=True, arguments=args) - - - # Cluster nodes: all nodes know about exchanges, bindings, but queues live on one node (unless mirrored/quorum) - - ' + code: null follow_ups: en: - How many nodes needed for a quorum queue? @@ -64,23 +50,7 @@ questions: time-based retention, offset tracking, super-streams, and use cases.' ru: 'Что такое RabbitMQ Streams? Чем они отличаются от классических очередей? Объясните возможности потоков: недеструктивное потребление, удержание по времени, отслеживание офсетов, супер-потоки и сценарии использования.' - code: '# Declare a stream (via plugin or client) - - # RabbitMQ 3.9+ with rabbitmq_stream plugin - - - args = {''x-queue-type'': ''stream'', ''x-max-length-bytes'': 10_000_000_000, ''x-stream-max-segment-size-bytes'': 500_000_000} - - channel.queue_declare(queue=''my_stream'', durable=True, arguments=args) - - - # Consume from a specific offset - - # offset: first, last, next, or numeric - - # Not applicable with basic.consume (stream uses special protocol) - - ' + code: null follow_ups: en: - How do streams compare with Kafka? @@ -112,27 +82,7 @@ questions: moving messages between clusters? What is federation for? Explain the use of rabbitmqadmin.' ru: 'Назовите важные плагины RabbitMQ: management, shovel, federation, delayed message exchange. Как использовать shovel для перемещения сообщений между кластерами? Для чего нужна федерация? Объясните использование rabbitmqadmin.' - code: '# Enable plugins - - rabbitmq-plugins enable rabbitmq_management rabbitmq_shovel rabbitmq_federation - - - # Shovel config (dynamic shovel via runtime parameter) - - rabbitmqctl set_parameter shovel my-shovel ''{"src-uri":"amqp://src","dest-uri":"amqp://dest","src-queue":"src-queue","dest-exchange":"dest-exchange"}'' - - - # Delayed Message Exchange plugin (not built-in, install separately) - - args = {''x-delayed-type'': ''direct''} - - channel.exchange_declare(''delayed_exchange'', ''x-delayed-message'', arguments=args) - - # Publish with delay - - props = pika.BasicProperties(headers={''x-delay'': 5000}) - - ' + code: null follow_ups: en: - What is the difference between shovel and federation? diff --git a/data/questions/rabbitmq/senior/operations.yaml b/data/questions/rabbitmq/senior/operations.yaml index 93ffbf1..027b83a 100644 --- a/data/questions/rabbitmq/senior/operations.yaml +++ b/data/questions/rabbitmq/senior/operations.yaml @@ -17,28 +17,7 @@ questions: and batch get, TCP settings, and lazy queues vs in-memory. ru: Как настроить RabbitMQ для высокой пропускной способности? Объясните контроль потока (credit-based), сигналы памяти, лимит свободного места на диске, prefetch и batch get, настройки TCP, а также ленивые очереди против in-memory. - code: '# rabbitmq.conf - - vm_memory_high_watermark.absolute = 2GB - - vm_memory_high_watermark_paging_ratio = 0.8 - - disk_free_limit.absolute = 2GB - - - # Increase file descriptors limit (ulimit -n 65536) - - # Use heartbeat to detect dead connections - - - # Client-side: use publisher confirms asynchronously, set prefetch=100-1000 (not 1) - - channel.basic_qos(prefetch_count=100) - - - # For streaming: use basic.get with multiple messages (not recommended, use consume) - - ' + code: null follow_ups: en: - What is the impact of persistent messages on performance? diff --git a/pyproject.toml b/pyproject.toml index e0f2456..1935761 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,5 +77,6 @@ testpaths = ["tests"] [dependency-groups] dev = [ + "pytest-cov>=7.1.0", "types-tqdm>=4.67.3.20260518", ] diff --git a/static/js/coding_complete.js b/static/js/coding_complete.js new file mode 100644 index 0000000..37278ae --- /dev/null +++ b/static/js/coding_complete.js @@ -0,0 +1,147 @@ +(function () { + "use strict"; + + const panel = document.getElementById("coding-panel"); + if (!panel) { + return; + } + + const interviewId = panel.dataset.interviewId || ""; + const llmRequestTimeoutSeconds = Number(panel.dataset.llmTimeout || 60); + + let completeWs = null; + let completeReconnectTimer = null; + let isEndingInterview = false; + let evaluationWatchdogTimer = null; + + const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:"; + const completeWsUrl = + wsProtocol + + "//" + + window.location.host + + "/interview/" + + encodeURIComponent(interviewId) + + "/theory/ws"; + + function showCompleteEvaluating(visible) { + const indicator = document.getElementById("coding-evaluating-indicator"); + if (indicator) { + indicator.hidden = !visible; + } + } + + function clearEvaluationWatchdog() { + if (evaluationWatchdogTimer) { + clearTimeout(evaluationWatchdogTimer); + evaluationWatchdogTimer = null; + } + } + + function startEvaluationWatchdog() { + clearEvaluationWatchdog(); + const graceMs = 15000; + const timeoutMs = llmRequestTimeoutSeconds * 1000 + graceMs; + evaluationWatchdogTimer = setTimeout(function () { + evaluationWatchdogTimer = null; + if (!isEndingInterview) { + return; + } + showCompleteEvaluating(false); + isEndingInterview = false; + const endBtn = document.getElementById("coding-end-btn"); + if (endBtn) { + endBtn.disabled = false; + } + alert( + "Final evaluation is taking too long. Check /config and try again." + ); + }, timeoutMs); + } + + function connectCompleteWebSocket() { + completeWs = new WebSocket(completeWsUrl); + + completeWs.onopen = function () { + if (completeReconnectTimer) { + clearTimeout(completeReconnectTimer); + completeReconnectTimer = null; + } + }; + + completeWs.onmessage = function (event) { + const data = JSON.parse(event.data); + if (data.type === "evaluating") { + showCompleteEvaluating(true); + startEvaluationWatchdog(); + } else if (data.type === "interview_completed") { + clearEvaluationWatchdog(); + showCompleteEvaluating(false); + window.location.href = + "/interview/" + + encodeURIComponent(interviewId) + + "/results"; + } else if (data.type === "error") { + clearEvaluationWatchdog(); + showCompleteEvaluating(false); + isEndingInterview = false; + const endBtn = document.getElementById("coding-end-btn"); + if (endBtn) { + endBtn.disabled = false; + } + alert(data.message || "Failed to complete interview."); + } + }; + + completeWs.onclose = function () { + if (isEndingInterview) { + clearEvaluationWatchdog(); + showCompleteEvaluating(false); + isEndingInterview = false; + const endBtn = document.getElementById("coding-end-btn"); + if (endBtn) { + endBtn.disabled = false; + } + } + completeReconnectTimer = setTimeout( + connectCompleteWebSocket, + 3000 + ); + }; + } + + function endCodingInterview() { + if (!confirm("Are you sure you want to end this interview?")) { + return; + } + if (isEndingInterview) { + return; + } + isEndingInterview = true; + const endBtn = document.getElementById("coding-end-btn"); + if (endBtn) { + endBtn.disabled = true; + } + showCompleteEvaluating(true); + startEvaluationWatchdog(); + if (!completeWs || completeWs.readyState !== WebSocket.OPEN) { + connectCompleteWebSocket(); + setTimeout(function () { + if (completeWs && completeWs.readyState === WebSocket.OPEN) { + completeWs.send( + JSON.stringify({ type: "complete" }) + ); + } + }, 500); + return; + } + completeWs.send(JSON.stringify({ type: "complete" })); + } + + document.addEventListener("DOMContentLoaded", function () { + connectCompleteWebSocket(); + const endBtn = document.getElementById("coding-end-btn"); + if (endBtn) { + endBtn.addEventListener("click", endCodingInterview); + } + }); +})(); \ No newline at end of file diff --git a/static/js/coding_session.js b/static/js/coding_session.js index 3bc127e..f4d79dc 100644 --- a/static/js/coding_session.js +++ b/static/js/coding_session.js @@ -477,6 +477,26 @@ }, }; + window.grillkitOnTimerExpired = function () { + if (!taskId || isSubmitting) { + return; + } + if (!ws || ws.readyState !== WebSocket.OPEN) { + showError("Timer expired but connection lost. Refresh to continue."); + return; + } + isSubmitting = true; + window.isSubmitting = true; + setComposerEnabled(false); + stopTaskTimer(); + ws.send( + JSON.stringify({ + type: "timeout", + task_id: taskId, + }) + ); + }; + document.addEventListener("DOMContentLoaded", function () { if (panel.dataset.complete === "true" || !taskId) { return; diff --git a/static/js/interview_audio_answer.js b/static/js/interview_audio_answer.js index 5172c5f..aa868f7 100644 --- a/static/js/interview_audio_answer.js +++ b/static/js/interview_audio_answer.js @@ -220,6 +220,7 @@ while (true) { const { done, value } = await reader.read(); if (done) { + console.log("[Audio] NDJSON stream done"); break; } buffer += decoder.decode(value, { stream: true }); @@ -230,15 +231,18 @@ if (!line) { continue; } + console.log("[Audio] NDJSON line:", line); dispatchInterviewEvent(JSON.parse(line)); } } const tail = buffer.trim(); if (tail) { + console.log("[Audio] NDJSON tail:", tail); dispatchInterviewEvent(JSON.parse(tail)); } } catch (err) { + console.error("[Audio] NDJSON stream error:", err); throw new Error( err && err.message ? err.message : "Audio answer stream failed" ); @@ -246,7 +250,8 @@ } function dispatchInterviewEvent(data) { - if (typeof window.grillkitHandleInterviewEvent === "function") { + console.log("[Audio] dispatchInterviewEvent:", data); + if (typeof window.grillkitHandleInterviewEvent === 'function') { window.grillkitHandleInterviewEvent(data); } } diff --git a/static/js/interview_timer.js b/static/js/interview_timer.js index 5714c2a..6bbea68 100644 --- a/static/js/interview_timer.js +++ b/static/js/interview_timer.js @@ -32,20 +32,22 @@ } function sendTimeout() { - if (timedOutSent || !getWs) { + if (timedOutSent) { return; } - if (window.isSubmitting) { + timedOutSent = true; + + if (typeof window.grillkitOnTimerExpired === "function") { + window.grillkitOnTimerExpired(); + } + + if (!getWs) { return; } const socket = getWs(); if (!socket || socket.readyState !== WebSocket.OPEN) { return; } - timedOutSent = true; - if (typeof window.grillkitOnTimerExpired === "function") { - window.grillkitOnTimerExpired(); - } socket.send( JSON.stringify({ type: "timeout", diff --git a/templates/coding_interview.html b/templates/coding_interview.html index 23058b6..92d3c35 100644 --- a/templates/coding_interview.html +++ b/templates/coding_interview.html @@ -100,8 +100,7 @@

All coding tasks submitted

{% block scripts %} {% if session_mode %} - + {% endif %} {% if coding.current_task %} + {% endif %} {% if coding.task_timer_enabled and interview.status == "active" %} {% endif %} {% if interview.status == "active" %} - + {% endif %} {% if interview.status == "active" and coding.current_task and not coding.complete %} diff --git a/templates/interview.html b/templates/interview.html index a812277..40dee26 100644 --- a/templates/interview.html +++ b/templates/interview.html @@ -90,7 +90,7 @@

{{ interview_title }}

{% if question_voice_enabled %}data-question-voice-enabled="true"{% endif %}>
{% for answer in answers %} - {% if answer.answer_text is not none or (current_question and answer.id == current_question.id) %} + {% if answer.answer_text is not none or answer.answer_text == '' or (current_question and answer.id == current_question.id) %}
@@ -114,7 +114,7 @@

{{ interview_title }}

- {% if answer.answer_text %} + {% if answer.answer_text is not none %}
You: {{ answer.answer_text }} @@ -323,6 +323,7 @@

{{ interview_title }}

} function handleWsMessage(data) { + console.log("[WS] handleWsMessage type:", data.type, "data:", data); switch (data.type) { case 'saved': break; @@ -333,10 +334,19 @@

{{ interview_title }}

break; case 'transcript': - if (typeof window.grillkitUpdateAudioAnswerBubble === 'function') { - window.grillkitUpdateAudioAnswerBubble(data.text); + if (!data.text) { + showError('Speech not recognized. Please try again or type your answer.'); + if (typeof window.grillkitUpdateAudioAnswerBubble === 'function') { + window.grillkitUpdateAudioAnswerBubble('[Not recognized]'); + } else { + showAnswerBubbleWithText('[Not recognized]'); + } } else { - showAnswerBubbleWithText(data.text); + if (typeof window.grillkitUpdateAudioAnswerBubble === 'function') { + window.grillkitUpdateAudioAnswerBubble(data.text); + } else { + showAnswerBubbleWithText(data.text); + } } break; diff --git a/tests/speech/services/test_dictation.py b/tests/speech/services/test_dictation.py index 7832ffd..9458c54 100644 --- a/tests/speech/services/test_dictation.py +++ b/tests/speech/services/test_dictation.py @@ -2,12 +2,10 @@ # SPDX-License-Identifier: Apache-2.0 """Tests for dictation speech recognition.""" -from unittest.mock import MagicMock import numpy as np import pytest -from app.ai.faster_whisper_transcriber import FasterWhisperTranscriber from app.speech.services.dictation import DictationSession from tests.helpers.transcription import FakeTranscriber @@ -38,25 +36,3 @@ async def test_finalize_uses_transcriber(self): assert transcriber.last_audio is not None assert transcriber.last_audio.dtype == np.float32 assert len(transcriber.last_audio) == 1600 - - -class TestFasterWhisperTranscriber: - """Tests for the faster-whisper adapter.""" - - @pytest.mark.asyncio - async def test_transcribe_calls_model(self): - """Adapter delegates to WhisperModel.transcribe with locale language.""" - segment = MagicMock() - segment.text = " hello" - model = MagicMock() - model.transcribe.return_value = ([segment], None) - - transcriber = FasterWhisperTranscriber(model) - audio = np.zeros(1600, dtype=np.float32) - text = await transcriber.transcribe(audio, "ru") - - assert text == "hello" - model.transcribe.assert_called_once() - call_kwargs = model.transcribe.call_args.kwargs - assert call_kwargs["language"] == "ru" - assert call_kwargs["task"] == "transcribe" diff --git a/uv.lock b/uv.lock index 1f75887..5e85f25 100644 --- a/uv.lock +++ b/uv.lock @@ -145,6 +145,90 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] +[[package]] +name = "coverage" +version = "7.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, + { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, + { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, + { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, + { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, + { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, + { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, + { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, + { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, + { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, + { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, + { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, + { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, + { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, + { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, + { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, + { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, + { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, + { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, + { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, + { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, + { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, + { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, + { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, + { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, + { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, + { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +] + [[package]] name = "ctranslate2" version = "4.7.2" @@ -315,6 +399,7 @@ dev = [ [package.dev-dependencies] dev = [ + { name = "pytest-cov" }, { name = "types-tqdm" }, ] @@ -343,7 +428,10 @@ requires-dist = [ provides-extras = ["dev"] [package.metadata.requires-dev] -dev = [{ name = "types-tqdm", specifier = ">=4.67.3.20260518" }] +dev = [ + { name = "pytest-cov", specifier = ">=7.1.0" }, + { name = "types-tqdm", specifier = ">=4.67.3.20260518" }, +] [[package]] name = "h11" @@ -1082,6 +1170,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, ] +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.2" From 6b80fd4bf9115357e081116fcff1682617a83072 Mon Sep 17 00:00:00 2001 From: vitchenkokir Date: Tue, 14 Jul 2026 16:58:39 +0300 Subject: [PATCH 2/4] docs: update changelog with timeout fix and question bank refactoring --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index af26e5f..f2c1ff8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,12 @@ Work in progress is accumulated under `[Unreleased]`; on release, that section b ### Changed +- **Question bank overhaul** — restructured and expanded question sets across all tracks (Python, Database, Airflow, Docker, Kubernetes, Observability, Kafka, RabbitMQ); updated questions map + ### Fixed +- **Session timeout** — fixed timer handling that caused premature session expiration or stuck rounds + ### Removed ## 2026.6.12 From b89c6fff899e9550c2ed048fec90c83ccd4b3f43 Mon Sep 17 00:00:00 2001 From: vitchenkokir Date: Tue, 14 Jul 2026 16:59:45 +0300 Subject: [PATCH 3/4] chore: release 2026.7.14 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c1ff8..a14c7fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,16 @@ Work in progress is accumulated under `[Unreleased]`; on release, that section b ### Changed +### Fixed + +### Removed + +## 2026.7.14 + +### Added + +### Changed + - **Question bank overhaul** — restructured and expanded question sets across all tracks (Python, Database, Airflow, Docker, Kubernetes, Observability, Kafka, RabbitMQ); updated questions map ### Fixed From 508b95baa6288fc535463d37464e2eea3548cd7e Mon Sep 17 00:00:00 2001 From: vitchenkokir Date: Tue, 14 Jul 2026 17:00:31 +0300 Subject: [PATCH 4/4] style: apply ruff formatting to test_dictation.py --- tests/speech/services/test_dictation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/speech/services/test_dictation.py b/tests/speech/services/test_dictation.py index 9458c54..0d01f3d 100644 --- a/tests/speech/services/test_dictation.py +++ b/tests/speech/services/test_dictation.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 """Tests for dictation speech recognition.""" - import numpy as np import pytest