[Domain Services в DDD: Логика, которая не принадлежит агрегатам]
(Когда и зачем использовать доменные сервисы?)
Продолжаю знакомиться с DDD. Следующий паттерн Domain Services.
Domain Service - это класс, который реализует бизнес-правила, выходящие за рамки одного агрегата.
В Domain-Driven Design (DDD) не вся бизнес-логика уместна внутри агрегатов или сущностей.
Иногда операции:
- Затрагивают несколько агрегатов,
- Зависят от внешних систем (например, проверка кредитного рейтинга),
- Не имеют естественного места в какой-то одной сущности.
Для такой логики создают Domain Services (доменные сервисы).
Отличия от других сервисов в DDD
Когда использовать Domain Service?
✅ Логика требует нескольких агрегатов.
✅ Зависит от доменных правил, но не принадлежит ни одной сущности (например, проверка сложных условий).
✅ Чистая бизнес-логика без инфраструктурных деталей.
❌ Не используйте, если:
- Логика относится к одному агрегату (лучше поместить в агрегат).
- Нужен доступ к БД, API и т.д. (это Application/Infrastructure Service).
✔️ Плюсы:
- Четкое разделение ответственности.
- Удобство тестирования (чистая логика без побочных эффектов).
❌ Минусы:
- Риск превращения в "God Object" (если сервис делает слишком много).
Domain Services — это мост между агрегатами для сложной доменной логики.
P.s.
Полезная ссылка по теме:
https://enterprisecraftsmanship.com/posts/domain-vs-application-services/
#DDD #DomainServices #CleanArchitecture
(Когда и зачем использовать доменные сервисы?)
Продолжаю знакомиться с DDD. Следующий паттерн Domain Services.
Domain Service - это класс, который реализует бизнес-правила, выходящие за рамки одного агрегата.
В Domain-Driven Design (DDD) не вся бизнес-логика уместна внутри агрегатов или сущностей.
Иногда операции:
- Затрагивают несколько агрегатов,
- Зависят от внешних систем (например, проверка кредитного рейтинга),
- Не имеют естественного места в какой-то одной сущности.
Для такой логики создают Domain Services (доменные сервисы).
Отличия от других сервисов в DDD
Domain Service
— содержит только бизнес-логику, не зависит от инфраструктуры.Application Service
— оркестрирует вызовы Domain-сервисов и инфраструктуры (например, «создать заказ → сохранить в БД → отправить уведомление»).Infrastructure Service
— технические детали (отправка почты, запросы к API).Когда использовать Domain Service?
✅ Логика требует нескольких агрегатов.
✅ Зависит от доменных правил, но не принадлежит ни одной сущности (например, проверка сложных условий).
✅ Чистая бизнес-логика без инфраструктурных деталей.
❌ Не используйте, если:
- Логика относится к одному агрегату (лучше поместить в агрегат).
- Нужен доступ к БД, API и т.д. (это Application/Infrastructure Service).
✔️ Плюсы:
- Четкое разделение ответственности.
- Удобство тестирования (чистая логика без побочных эффектов).
❌ Минусы:
- Риск превращения в "God Object" (если сервис делает слишком много).
Domain Services — это мост между агрегатами для сложной доменной логики.
P.s.
Полезная ссылка по теме:
https://enterprisecraftsmanship.com/posts/domain-vs-application-services/
#DDD #DomainServices #CleanArchitecture
👍3
✅ 120 кг в жиме лёжа;
(мой вес 82,5 кг)
Недели 3 назад сделал несколько безуспешных попыток. Далее сказал себе, что не подойду к этому весу пока не буду уверенно работать со штангой 110 кг.
И вот позавчера (31 марта) впервые взял этот вес.
Получил много эмоций от достижения очередного результата)
#gym #life
(мой вес 82,5 кг)
Недели 3 назад сделал несколько безуспешных попыток. Далее сказал себе, что не подойду к этому весу пока не буду уверенно работать со штангой 110 кг.
И вот позавчера (31 марта) впервые взял этот вес.
Получил много эмоций от достижения очередного результата)
#gym #life
🔥10
[Памятка: как делать правильные тесты]
Раньше я писал юнит-тесты как будто "на автомате" — просто покрывал методы, проверял, что возвращается нужное значение, и считал, что всё ок.
Но это не совсем правильно. Тестировать методы ради покрытия — выглядит логично, но часто оказывается поверхностным и ненадёжным.
Например, я тестирую имплементацию, а не поведение.
Но при рефакторинге метод может быть переписан — и старый тест либо ломается (хотя логика осталась верной), либо ничего не заметит, если баг спрятался глубже.
Невозможно быстро понять по тестам, что именно гарантирует система.
Тесты по методам не объясняют, что система должна делать — только что она делает сейчас.
А свойства — это живое описание инвариантов кода. И это крайне важно.
Правильный подход — тестировать свойства поведения фичи, которые должны сохраняться независимо от реализации.
Вот что я теперь делаю:
1) Останавливаюсь и думаю: что вообще значит "правильно работает"?
Не метод за методом, а вся фича в целом.
2) Формулирую свойства корректности:
Например, "если policy создана — она должна быть доступна", "удаление policy делает её недоступной", и т.д.
3) Для каждого свойства выбираю способ тестирования:
Если можно покрыть юнит-тестами — пишу их. Если нужно — делаю фаззинг или просто руками проверяю.
4) Пишу тесты, которые проверяют именно эти свойства, а не случайные детали реализации.
Что изменилось?
- Тесты стало проще поддерживать: они не ломаются из-за мелких рефакторингов.
- Я лучше понимаю, зачем вообще эта фича нужна, а не просто "чтобы метод не падал".
- Реже ловлю баги в проде — потому что я реально проверяю смысл, а не поведение "по случайке".
#unittests #python #testdesign #tests
Раньше я писал юнит-тесты как будто "на автомате" — просто покрывал методы, проверял, что возвращается нужное значение, и считал, что всё ок.
Но это не совсем правильно. Тестировать методы ради покрытия — выглядит логично, но часто оказывается поверхностным и ненадёжным.
Например, я тестирую имплементацию, а не поведение.
Но при рефакторинге метод может быть переписан — и старый тест либо ломается (хотя логика осталась верной), либо ничего не заметит, если баг спрятался глубже.
Невозможно быстро понять по тестам, что именно гарантирует система.
Тесты по методам не объясняют, что система должна делать — только что она делает сейчас.
А свойства — это живое описание инвариантов кода. И это крайне важно.
Правильный подход — тестировать свойства поведения фичи, которые должны сохраняться независимо от реализации.
Вот что я теперь делаю:
1) Останавливаюсь и думаю: что вообще значит "правильно работает"?
Не метод за методом, а вся фича в целом.
2) Формулирую свойства корректности:
Например, "если policy создана — она должна быть доступна", "удаление policy делает её недоступной", и т.д.
3) Для каждого свойства выбираю способ тестирования:
Если можно покрыть юнит-тестами — пишу их. Если нужно — делаю фаззинг или просто руками проверяю.
4) Пишу тесты, которые проверяют именно эти свойства, а не случайные детали реализации.
Что изменилось?
- Тесты стало проще поддерживать: они не ломаются из-за мелких рефакторингов.
- Я лучше понимаю, зачем вообще эта фича нужна, а не просто "чтобы метод не падал".
- Реже ловлю баги в проде — потому что я реально проверяю смысл, а не поведение "по случайке".
#unittests #python #testdesign #tests
👍3
[Репозитории в DDD: как правильно работать с данными в доменном слое]
Привет! Сегодня разберём ещё одну важную концепцию Domain-Driven Design — Репозитории (Repositories). Когда я только начал изучать DDD, репозитории казались мне просто "прослойкой над базой данных". Давайте разберёмся, зачем они нужны и как их правильно использовать!
Репозиторий (Repository) — это абстракция, которая:
✔️ Инкапсулирует доступ к данным (БД, API, файлы и т. д.).
✔️ Предоставляет "коллекцию" доменных объектов (как если бы они хранились в памяти).
✔️ Работает только с Aggregate Roots (не с отдельными Entity или Value Object!).
Пример из жизни:
Если
- Сохранять заказ (save).
- Загружать заказ по ID (find_by_id).
- Искать заказы по критериям (find_all_by_customer).
Но он НЕ даёт прямого доступа к
Ниже пример кода.
Почему репозитории — это важно?
1) Отделение домена от инфраструктуры
Домен не знает, как хранятся данные (БД, API, CSV и т. д.).
2) Гибкость
Можно легко поменять базу данных, не трогая доменную логику.
3) Соблюдение DDD-принципов
Работа только с агрегатами, а не с отдельными таблицами БД.
Где должны находиться репозитории в структуре проекта?
Репозитории относятся к инфраструктурному слою, но их интерфейсы (абстракции) определяются в доменном слое.
Ниже пример классической структуры проекта по слоям DDD.
```bash
src/
├── domain/
│ ├── models/
│ ├── repositories/
│ └── services/
│
├── infrastructure/
│ ├── db/
│ ├── repositories/
│ └── caching/
│
├── application/
│ └── services/
│
└── presentation/
```
Типичные ошибки
❌ Репозиторий для каждой Entity
Репозиторий должен быть только для Aggregate Root!
❌ Возвращение DTO вместо доменных объектов
Репозиторий должен возвращать
❌ Нарушение CQRS
Для запросов (Read) иногда лучше использовать отдельные запросы в cлое приложения, а не репозитории.
P.s.
Полезные ссылки по теме:
1) https://dddinpython.hashnode.dev/implementing-the-repository-pattern
2) https://dddinpython.hashnode.dev/implementing-the-repository-pattern-using-sqlalchemy
#DDD #Repository #DomainDrivenDesign #Программирование #Python
Привет! Сегодня разберём ещё одну важную концепцию Domain-Driven Design — Репозитории (Repositories). Когда я только начал изучать DDD, репозитории казались мне просто "прослойкой над базой данных". Давайте разберёмся, зачем они нужны и как их правильно использовать!
Репозиторий (Repository) — это абстракция, которая:
✔️ Инкапсулирует доступ к данным (БД, API, файлы и т. д.).
✔️ Предоставляет "коллекцию" доменных объектов (как если бы они хранились в памяти).
✔️ Работает только с Aggregate Roots (не с отдельными Entity или Value Object!).
Пример из жизни:
Если
Order
— это агрегат, то OrderRepository позволяет:- Сохранять заказ (save).
- Загружать заказ по ID (find_by_id).
- Искать заказы по критериям (find_all_by_customer).
Но он НЕ даёт прямого доступа к
OrderItem
— только через корень (Order
).Ниже пример кода.
from abc import ABC, abstractmethod
from uuid import UUID
from typing import List, Optional
from .order import Order # Агрегат
class OrderRepository(ABC):
"""Абстракция репозитория"""
@abstractmethod
def save(self, order: Order) -> None:
pass
@abstractmethod
def find_by_id(self, order_id: UUID) -> Optional[Order]:
pass
@abstractmethod
def find_all_by_customer(self, customer_id: UUID) -> List[Order]:
pass
from sqlalchemy.orm import Session
class SQLOrderRepository(OrderRepository):
"""SQL реализация (через SQLAlchemy)"""
def __init__(self, session: Session):
self.session = session
def save(self, order: Order) -> None:
self.session.add(order)
self.session.commit()
def find_by_id(self, order_id: UUID) -> Optional[Order]:
return self.session.query(Order).filter_by(order_id=order_id).first()
def find_all_by_customer(self, customer_id: UUID) -> List[Order]:
return self.session.query(Order).filter_by(customer_id=customer_id).all()
Почему репозитории — это важно?
1) Отделение домена от инфраструктуры
Домен не знает, как хранятся данные (БД, API, CSV и т. д.).
2) Гибкость
Можно легко поменять базу данных, не трогая доменную логику.
3) Соблюдение DDD-принципов
Работа только с агрегатами, а не с отдельными таблицами БД.
Где должны находиться репозитории в структуре проекта?
Репозитории относятся к инфраструктурному слою, но их интерфейсы (абстракции) определяются в доменном слое.
Ниже пример классической структуры проекта по слоям DDD.
```bash
src/
├── domain/
│ ├── models/
│ ├── repositories/
│ └── services/
│
├── infrastructure/
│ ├── db/
│ ├── repositories/
│ └── caching/
│
├── application/
│ └── services/
│
└── presentation/
```
Типичные ошибки
❌ Репозиторий для каждой Entity
Репозиторий должен быть только для Aggregate Root!
❌ Возвращение DTO вместо доменных объектов
Репозиторий должен возвращать
Order
, а не OrderData
.❌ Нарушение CQRS
Для запросов (Read) иногда лучше использовать отдельные запросы в cлое приложения, а не репозитории.
P.s.
Полезные ссылки по теме:
1) https://dddinpython.hashnode.dev/implementing-the-repository-pattern
2) https://dddinpython.hashnode.dev/implementing-the-repository-pattern-using-sqlalchemy
#DDD #Repository #DomainDrivenDesign #Программирование #Python
👍3
[Use Case в DDD: как проектировать сценарии взаимодействия с системой]
Привет! Сегодня поговорим про Use Case (Сценарии использования) — один из ключевых элементов DDD и чистой архитектуры.
1. Что такое Use Case?
Use Case (Сценарий использования) — это:
✔️ Описание конкретного бизнес-действия (например, "Оформление заказа", "Отмена бронирования").
✔️ Изолированная логика, которая координирует работу домена и инфраструктуры.
✔️ Мост между presentation (API/UI) и domain (бизнес-правила).
Примеры:
2. Где находятся Use Case в структуре проекта?
Они относятся к application-слою (слое приложения):
3. Пример use case "Создание заказа"
4. Почему Use Case — это важно?
- Чёткое разделение ответственности
Presentation-слой (API/CLI) не должен содержать бизнес-логику.
Domain-слой не должен знать о внешних сервисах (API, БД).
- Упрощение тестирования
Use Case можно тестировать изолированно, подменяя репозитории на заглушки.
- Гибкость
Один Use Case может быть вызван из разных мест: API, CLI, фоновой задачи.
- Документирование системы
Названия Use Cases (CreateOrder, CancelBooking) явно описывают, что делает система.
5. Типичные ошибки
❌ Use Case = "God Object"
Не нужно пихать всю логику в один Use Case. Разбивайте на мелкие сценарии.
❌ Логика в контроллерах
Код вида
#DDD #UseCase #CleanArchitecture #Python
Привет! Сегодня поговорим про Use Case (Сценарии использования) — один из ключевых элементов DDD и чистой архитектуры.
1. Что такое Use Case?
Use Case (Сценарий использования) — это:
✔️ Описание конкретного бизнес-действия (например, "Оформление заказа", "Отмена бронирования").
✔️ Изолированная логика, которая координирует работу домена и инфраструктуры.
✔️ Мост между presentation (API/UI) и domain (бизнес-правила).
Примеры:
CreateOrderUseCase
→ создание заказа.CancelBookingUseCase
→ отмена бронирования.ProcessPaymentUseCase
→ обработка платежа.2. Где находятся Use Case в структуре проекта?
Они относятся к application-слою (слое приложения):
src/
├── domain/
├── application/ # Use Cases + Application Services/CQS
│ ├── use_cases/
│ │ ├── commands/
│ │ └── queries/
│ └── services/
├── infrastructure/
└── api/
3. Пример use case "Создание заказа"
# /delivery/core/application/use_cases/commands/create_order/create_order_handler.py
from dataclasses import dataclass
from core.domain.model.order_aggregate.order import Order
from core.domain.model.shared_kernel.location import Location
from core.application.use_cases.commands.create_order.create_order_command import CreateOrderCommand
from infrastructure.adapters.postgres.unit_of_work import UnitOfWork
@dataclass
class CreateOrderCommandHandler:
unit_of_work: UnitOfWork
def handle(self, massege: CreateOrderCommand) -> None:
# Получаем геопозицию из Geo (пока ставим рандомное значение)
location = Location.create_random_location()
# Создаем заказ
order = Order(
order_id=massege.basket_id, # ID заказа совпадает с ID корзины
location=location
)
# Сохраняем
with self.unit_of_work as uow:
uow.orders.add(order)
4. Почему Use Case — это важно?
- Чёткое разделение ответственности
Presentation-слой (API/CLI) не должен содержать бизнес-логику.
Domain-слой не должен знать о внешних сервисах (API, БД).
- Упрощение тестирования
Use Case можно тестировать изолированно, подменяя репозитории на заглушки.
- Гибкость
Один Use Case может быть вызван из разных мест: API, CLI, фоновой задачи.
- Документирование системы
Названия Use Cases (CreateOrder, CancelBooking) явно описывают, что делает система.
5. Типичные ошибки
❌ Use Case = "God Object"
Не нужно пихать всю логику в один Use Case. Разбивайте на мелкие сценарии.
❌ Логика в контроллерах
Код вида
if user.is_admin: ...
должен быть в Use Case или домене, не в API.#DDD #UseCase #CleanArchitecture #Python
👍1
[Input Adapters в DDD: как внешний мир общается с приложением]
Привет! Сегодня про Input Adapters — элемент гексагональной архитектуры (и DDD), который отвечает за преобразование внешних запросов (HTTP, CLI, события) в команды приложения.
1. Что это такое?
Input Adapter — это не только про REST-эндпоинты, а это единая точка входа для всех внешних взаимодействий.
Он:
- Парсит входящие данные (JSON, protobuf, CLI-аргументы).
- Валидирует их (например, через Pydantic).
- Вызывает Use Case (Command или Query).
- Не содержит бизнес-логики (это задача домена).
Примеры Input Adapters:
- REST API (FastAPI, Django View).
- CLI-команды (Click, Typer).
- Обработчики событий (Kafka Consumer).
- WebSocket-хэндлеры.
Ниже пример REST API
2. Где находятся Input Adapters в структуре проекта?
Они относятся к presentation-слою, но могут выноситься в отдельный модуль для сложных сценариев:
3. Почему Input Adapters — это важно?
- Изоляция домена
Домен не знает, откуда пришёл запрос (HTTP, Kafka или CLI).
- Гибкость
Можно добавить новый адаптер (например, gRPC), не меняя Use Case.
- Тестируемость
Адаптеры легко тестировать в изоляции (например, мокая Use Case).
- Согласованность
Все входные данные проходят одинаковую валидацию перед передачей в Use Case.
4. Типичные ошибки
❌ Бизнес-логика в адаптерах
Код вида if user.role == "admin" должен быть в домене, а не в REST-хэндлере.
❌ Прямая работа с репозиториями
Адаптер вызывает только Use Case, не лезет в БД напрямую.
5. Вывод
Input Adapters — это "входные ворота" приложения, которые:
🔹 Преобразуют внешние запросы в команды/запросы.
🔹 Не содержат логики (только парсинг и валидацию).
🔹 Делают систему гибкой к изменениям протоколов.
#DDD #CleanArchitecture #InputAdapters #Python
Привет! Сегодня про Input Adapters — элемент гексагональной архитектуры (и DDD), который отвечает за преобразование внешних запросов (HTTP, CLI, события) в команды приложения.
1. Что это такое?
Input Adapter — это не только про REST-эндпоинты, а это единая точка входа для всех внешних взаимодействий.
Он:
- Парсит входящие данные (JSON, protobuf, CLI-аргументы).
- Валидирует их (например, через Pydantic).
- Вызывает Use Case (Command или Query).
- Не содержит бизнес-логики (это задача домена).
Примеры Input Adapters:
- REST API (FastAPI, Django View).
- CLI-команды (Click, Typer).
- Обработчики событий (Kafka Consumer).
- WebSocket-хэндлеры.
Ниже пример REST API
# presentation/api/rest/order_adapters.py
from fastapi import APIRouter, Depends
from application.commands import CreateOrderCommand
from presentation.schemas import OrderCreateRequest
router = APIRouter()
@router.post("/orders")
async def create_order(
request: OrderCreateRequest, # Pydantic-модель для валидации
command: CreateOrderCommand = Depends(), # Use Case
):
# Преобразуем запрос в DTO (без бизнес-логики!)
order_id = command.execute(
user_id=request.user_id,
items=[item.to_domain() for item in request.items]
)
return {"order_id": order_id}
2. Где находятся Input Adapters в структуре проекта?
Они относятся к presentation-слою, но могут выноситься в отдельный модуль для сложных сценариев:
src/
├── domain/
├── application/
├── infrastructure/
└── presentation/ # Input Adapters
├── api/ # HTTP-адаптеры
│ ├── rest/ # REST (FastAPI)
│ └── graphql/ # GraphQL
├── cli/ # CLI-адаптеры (Click)
└── events/ # Обработчики событий (Kafka, RabbitMQ)
3. Почему Input Adapters — это важно?
- Изоляция домена
Домен не знает, откуда пришёл запрос (HTTP, Kafka или CLI).
- Гибкость
Можно добавить новый адаптер (например, gRPC), не меняя Use Case.
- Тестируемость
Адаптеры легко тестировать в изоляции (например, мокая Use Case).
- Согласованность
Все входные данные проходят одинаковую валидацию перед передачей в Use Case.
4. Типичные ошибки
❌ Бизнес-логика в адаптерах
Код вида if user.role == "admin" должен быть в домене, а не в REST-хэндлере.
❌ Прямая работа с репозиториями
Адаптер вызывает только Use Case, не лезет в БД напрямую.
5. Вывод
Input Adapters — это "входные ворота" приложения, которые:
🔹 Преобразуют внешние запросы в команды/запросы.
🔹 Не содержат логики (только парсинг и валидацию).
🔹 Делают систему гибкой к изменениям протоколов.
#DDD #CleanArchitecture #InputAdapters #Python
👍3
[📌 Кейс из практики: как я восстановил работу таблицы в HDFS]
Порой я не знаю о чем написать. Рабочие моменты кажутся скучными. Но в этот раз я решил поделиться поучительной историей.
📉 Пришли с проблемой: отчёт в Power BI больше не работает — запросы просто падают по таймауту.
🔍 Начал разбираться и выяснил, что таблица в HDFS содержит более 75 000 мелких файлов на 477 партиций.
Причина — неудачная логика записи: в каждую партицию складывалось множество крошечных файлов.
Также полностью отсутствовала статистика по таблице.
❌ Почему это проблема:
- Каждый файл — это нагрузка на NameNode. Когда их десятки тысяч, планирование и выполнение запросов начинают страдать.
- Без статистики оптимизатор не может строить адекватные планы выполнения — особенно в системах вроде Hive или Presto.
- Мелкие файлы хуже сжимаются. Компрессия эффективнее на крупных блоках, поэтому итоговый объём данных на диске получается больше, чем при хранении тех же данных в виде агрегированных файлов.
✅ Решения и результаты:
1. Пересчитал статистику по таблице.
После этого таблица "ожила" — запросы перестали падать, выполнялись за ~220 секунд. Уже хорошо. Пользователь уже был очень доволен.
2. Провёл частичную компакцию (объединение файлов).
Сократил количество файлов до 1900 (на 477 партиций), пересчитал статистику повторно — и запрос начал выполняться за 120 секунд.
3. Запустил полную компакцию.
Сделал по одному файлу на каждую партицию (итого 477 файлов). После очередного пересчёта статистики:
- Первый запуск запроса — 58 секунд
- Повторный — 17 секунд!
- В Trino — практически мгновенное выполнение
💾 Также заметно уменьшился размер таблицы на диске — компрессия начала работать в полную силу.
💡 Выводы:
🔹 Мелкие файлы = деградация производительности + перерасход хранилища.
🔹 Регулярная компакция + расчет статистики = must-have для стабильной работы.
🔹 Даже простые меры могут дать существенный прирост производительности и повысить удовлетворённость пользователей.
Теперь в этой таблице порядок, запросы работают стабильно и быстро, пользователь в полном восторге.😊
#bigdata #hdfs #datalake #hive #dataengineering #hadoop
Порой я не знаю о чем написать. Рабочие моменты кажутся скучными. Но в этот раз я решил поделиться поучительной историей.
📉 Пришли с проблемой: отчёт в Power BI больше не работает — запросы просто падают по таймауту.
🔍 Начал разбираться и выяснил, что таблица в HDFS содержит более 75 000 мелких файлов на 477 партиций.
Причина — неудачная логика записи: в каждую партицию складывалось множество крошечных файлов.
Также полностью отсутствовала статистика по таблице.
❌ Почему это проблема:
- Каждый файл — это нагрузка на NameNode. Когда их десятки тысяч, планирование и выполнение запросов начинают страдать.
- Без статистики оптимизатор не может строить адекватные планы выполнения — особенно в системах вроде Hive или Presto.
- Мелкие файлы хуже сжимаются. Компрессия эффективнее на крупных блоках, поэтому итоговый объём данных на диске получается больше, чем при хранении тех же данных в виде агрегированных файлов.
✅ Решения и результаты:
1. Пересчитал статистику по таблице.
ANALYZE TABLE имя_таблицы COMPUTE STATISTICS;
После этого таблица "ожила" — запросы перестали падать, выполнялись за ~220 секунд. Уже хорошо. Пользователь уже был очень доволен.
2. Провёл частичную компакцию (объединение файлов).
Сократил количество файлов до 1900 (на 477 партиций), пересчитал статистику повторно — и запрос начал выполняться за 120 секунд.
3. Запустил полную компакцию.
Сделал по одному файлу на каждую партицию (итого 477 файлов). После очередного пересчёта статистики:
- Первый запуск запроса — 58 секунд
- Повторный — 17 секунд!
- В Trino — практически мгновенное выполнение
💾 Также заметно уменьшился размер таблицы на диске — компрессия начала работать в полную силу.
💡 Выводы:
🔹 Мелкие файлы = деградация производительности + перерасход хранилища.
🔹 Регулярная компакция + расчет статистики = must-have для стабильной работы.
🔹 Даже простые меры могут дать существенный прирост производительности и повысить удовлетворённость пользователей.
Теперь в этой таблице порядок, запросы работают стабильно и быстро, пользователь в полном восторге.😊
#bigdata #hdfs #datalake #hive #dataengineering #hadoop
🔥7👍1
[💪Соревнования по жиму лежа и новый личный рекорд]
Продолжаю ходить в тренажерный зал. Есть новые результаты:
1) Занял 2-е место в местных соревнованиях по жиму лежа с результатом 120 кг (для меня это первое подобное мероприятие);
2) Побил личный рекорд, взяв 125 кг (правда эта попытка была уже вне конкурса);
P.s. собственный вес 83,8 кг.
#gym #life
Продолжаю ходить в тренажерный зал. Есть новые результаты:
1) Занял 2-е место в местных соревнованиях по жиму лежа с результатом 120 кг (для меня это первое подобное мероприятие);
2) Побил личный рекорд, взяв 125 кг (правда эта попытка была уже вне конкурса);
P.s. собственный вес 83,8 кг.
#gym #life
🔥10👏5🏆3