Высокопроизводительный сервис на Go для создания коротких ссылок и отслеживания аналитики переходов в режиме реального времени. Приложение спроектировано по принципам Clean Architecture (Чистой архитектуры) и оптимизировано для работы под высокой нагрузкой.
- Генерация уникальных кодов: Быстрое создание коротких идентификаторов для длинных URL.
- Высокопроизводительная аналитика (Async Clicks): Учет кликов по ссылкам вынесен в асинхронный воркер через Go-каналы (
chan string). Это позволяет отдавать оригинальный URL пользователю мгновенно, не дожидаясь блокирующих операций записи аналитики в базу данных. - Чистая архитектура: Четкое разделение ответственности на слои (Интерфейсы, UseCase, Репозитории/Сущности), что упрощает поддержку и тестирование кода.
- Тестируемость (Table-Driven Tests): Более 50% кодовой базы покрыто тестами с использованием табличного подхода и моков.
- Language: Go 1.26+
- DataBase: PostgreSQL, Redis
- Testing: unit testing, benchmark testing, mocking
Для обеспечения минимального времени задержки (Latency) при редиректе, инкремент счетчика переходов выполняется в фоновом режиме:
- При вызове
GetLongURL(ctx, shortCode)сервис извлекает ссылку из кэша/БД. - Код ссылки отправляется в буферизированный канал:
uc.clicksChan <- shortCode. - Фоновый воркер (worker) читает из канала и пакетами или поштучно обновляет данные в хранилище.
- Пользователь получает ответ немедленно, минуя ожидание транзакции БД.
Бизнес-логика (usecase) протестирована изолированно от внешних зависимостей (баз данных, кэша) с помощью интерфейсов и использования мок-объектов. Тесты проверяют как успешные сценарии, так и обработку ошибок со стороны слоя данных.
Для запуска всех юнит-тестов в слое usecase выполните команду:
go test -v ./internal/usecase/...Для запуска тестов с проверкой отсутствия состояний гонки (Race Condition):
go test -race ./internal/usecase/...Схема назначения короткого кода ссылки, заменена с последовательной: INSERT в базу -> генерация кода из id -> UPDATE кода в базу на генерацию числа для генерации кода в слое бизнес-логики(Snowflake ID): генерация ID -> генерация кода -> INSERT в базу
Это позволило:
- Сократить кол-во запросов в базу
- Уменьшить время выполнения операции
- Уменьшить кол-во памяти требуемой на операцию
- Повысить безопасность короткой ссылки, тк теперь коды расположены не по порядку и просто так перебрать коды не получится
Для замера эффективности перехода от схемы INSERT + UPDATE (последовательный ID) к схеме 1 INSERT (Snowflake ID) были проведены макро-бенчмарки слоя бизнес-логики в связке с pgxpool (PostgreSQL).
Окружение:
- OS/Arch:
darwin/arm64(Apple M1) - Пакет:
shortener/internal/usecase
| Подход | Скорость (ns/op) | Память (B/op) | Аллокации (allocs/op) |
|---|---|---|---|
| Old (2 запроса: Ins + Upd) | 1.117.467 ns/op | 5.413 B/op | 107 allocs/op |
| New (1 запрос: Snowflake) | 610.794 ns/op | 2,140 B/op | 45 allocs/op |
- Скорость обработки запроса выросла на ~45%.
- Выделение памяти в куче уменьшилось на ~61%.
- Нагрузка на Garbage Collector (аллокации) снизилась в 2.4 раза.
Самостоятельно этот тест можно запустить с помощью:
> make testUsecase