chore: initial import from sless/shared-sqs (v0.1.14)
- Standalone SQS-service repository - Multi-tenant message queue service, AWS SQS compatible - Based on GoAws, with mutable tenants, auth, WebUI, Redis persistence - Ready for independent development and deployment - See doc/ and README.md for architecture and usage
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# BYOC — Bring Your Own Credentials
|
||||
|
||||
**Дата**: 2026-04-09
|
||||
**Статус**: частично реализовано (backend), UI/API — TODO
|
||||
|
||||
---
|
||||
|
||||
## Суть
|
||||
|
||||
Возможность создать тенанта с **произвольными** Access Key и Secret Key вместо авто-генерируемых.
|
||||
|
||||
Нужно для:
|
||||
- демо-стенда с фиксированными credentials (README всегда актуален)
|
||||
- интеграционных тестов с предсказуемыми значениями
|
||||
- миграции с другого SQS-совместимого сервиса (сохранение существующих ключей)
|
||||
|
||||
---
|
||||
|
||||
## Что уже сделано
|
||||
|
||||
### `app/tenant/tenant_store.go` — `CreateFixed`
|
||||
|
||||
```go
|
||||
func (s *TenantStore) CreateFixed(
|
||||
name string,
|
||||
maxQueues int,
|
||||
tenantID string,
|
||||
accessKey string,
|
||||
secretKey string,
|
||||
) (*Tenant, error)
|
||||
```
|
||||
|
||||
Создаёт тенанта с заранее известными credentials.
|
||||
Проверяет уникальность и `tenantID`, и `accessKey` — конфликт возвращает ошибку.
|
||||
|
||||
### `app/cmd/seed.go` — демо-тенант
|
||||
|
||||
Использует `CreateFixed` при `SHARED_SQS_SEED_DEMO=true`:
|
||||
|
||||
```
|
||||
tenantID = "t-demo-shared-sqs-ngcloud"
|
||||
accessKey = "SSAK-demo-shared-sqs"
|
||||
secretKey = "demo-secret-key-shared-sqs-ngcloud-2026"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что нужно сделать (TODO)
|
||||
|
||||
### Admin API — `POST /admin/tenants`
|
||||
|
||||
Добавить в `createTenantRequest` два опциональных поля:
|
||||
|
||||
```go
|
||||
// app/admin/admin.go
|
||||
type createTenantRequest struct {
|
||||
Name string `json:"name"`
|
||||
MaxQueues int `json:"max_queues"`
|
||||
AccessKey string `json:"access_key,omitempty"` // TODO: BYOC
|
||||
SecretKey string `json:"secret_key,omitempty"` // TODO: BYOC
|
||||
}
|
||||
```
|
||||
|
||||
Логика в `createTenant` handler:
|
||||
|
||||
```go
|
||||
var t *tenant.Tenant
|
||||
var err error
|
||||
if req.AccessKey != "" || req.SecretKey != "" {
|
||||
// BYOC: оба поля обязательны
|
||||
if req.AccessKey == "" || req.SecretKey == "" {
|
||||
jsonErr(w, http.StatusBadRequest, "both access_key and secret_key required when specifying custom credentials")
|
||||
return
|
||||
}
|
||||
// Минимальная длина — защита от случайно слабых ключей
|
||||
if len(req.AccessKey) < 8 || len(req.SecretKey) < 16 {
|
||||
jsonErr(w, http.StatusBadRequest, "access_key min 8 chars, secret_key min 16 chars")
|
||||
return
|
||||
}
|
||||
t, err = h.store.CreateFixed(req.Name, req.MaxQueues, generateTenantID(), req.AccessKey, req.SecretKey)
|
||||
} else {
|
||||
t, err = h.store.Create(req.Name, req.MaxQueues)
|
||||
}
|
||||
```
|
||||
|
||||
> `generateTenantID()` — уже есть в tenant_store.go, нужно экспортировать или вынести.
|
||||
|
||||
### UI — Web форма создания тенанта
|
||||
|
||||
- Добавить в модальное окно "Создать тенанта" два опциональных поля: Access Key, Secret Key
|
||||
- Показывать только если нажата кнопка "задать свои credentials"
|
||||
- Валидация на клиенте: оба поля заполнены, мин. длина
|
||||
|
||||
---
|
||||
|
||||
## Безопасность
|
||||
|
||||
- BYOC-credentials **не дают доступа к admin API** — admin защищён отдельным Bearer токеном
|
||||
- Тенант видит **только свои очереди** — изоляция по AccessKey в auth middleware
|
||||
- Слабые ключи отклоняются на уровне API (минимальная длина)
|
||||
- Credentials передаются только по HTTPS
|
||||
|
||||
---
|
||||
|
||||
## Демо-credentials (открыты намеренно)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Access Key** | `SSAK-demo-shared-sqs` |
|
||||
| **Secret Key** | `demo-secret-key-shared-sqs-ngcloud-2026` |
|
||||
| **Tenant ID** | `t-demo-shared-sqs-ngcloud` |
|
||||
| **Лимит очередей** | 10 |
|
||||
|
||||
Эти credentials жёстко прописаны в `app/cmd/seed.go`.
|
||||
Тенант создаётся только если `SHARED_SQS_SEED_DEMO=true` (env var в deployment.yaml).
|
||||
@@ -0,0 +1,79 @@
|
||||
# SQS-service Progress
|
||||
|
||||
## Версага v0.1.x
|
||||
|
||||
### v0.1.14 (2026-04-10) ✅
|
||||
- ✅ Фикс критического дедлока в `create_queue.go` (deadlock после первого CreateQueue)
|
||||
- ✅ Фикс UI: `m.sent` → `m.sent_at` (даты сообщений всегда показывали dash)
|
||||
- ✅ Redis write-through persistence запущен
|
||||
- ✅ TLS Ingress: `qu.kube5s.ru` → `185.247.187.151`
|
||||
- ✅ Нагрузочный тест Harbor: 4757 запросов, 99% успех, p95=332ms
|
||||
- **Status: Production-ready для демо, готов к выводу за скобки в отдельный репо**
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [ ] Migration guide: как обновить сервис на v0.1.14
|
||||
- [ ] Load testing shared-sqs (текущая реализация имеет глобальный мьютекс)
|
||||
- [ ] DLQ (Dead Letter Queue) поддержка
|
||||
- [ ] Long Polling оптимизация
|
||||
- [ ] Rate limiting per tenant
|
||||
|
||||
## Known Limitations
|
||||
|
||||
1. **Глобальный мьютекс** — SyncQueues.Lock() на весь сервис. Performance bottleneck.
|
||||
2. **Нет DLQ** — failed messages теряются
|
||||
3. **Long Polling naïve** — polling каждую 100ms вместо push-based
|
||||
4. **Нет rate limiting** — один тенант может забить всех
|
||||
5. **Single pod** — нет горизонтального масштабирования
|
||||
6. **Нет метрик** — Prometheus экспортер отсутствует
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
SQS-service/
|
||||
├── app/
|
||||
│ ├── cmd/main.go — точка входа
|
||||
│ ├── models/model.go — Queue, Message, Tenant структуры
|
||||
│ ├── gosqs/*.go — реализация SQS API операций
|
||||
│ ├── admin/admin.go — Admin API (Create/List/Get Tenant)
|
||||
│ ├── tenant/tenant.go — Multi-tenant изоляция
|
||||
│ ├── auth/auth.go — AWS Signature V4 верификация
|
||||
│ ├── persistence/redis.go — Redis adapter для persistence
|
||||
│ ├── router/router.go — HTTP маршруты
|
||||
│ └── ui/index.html — Web Console
|
||||
├── deployments/k8s/ — Kubernetes манифесты
|
||||
├── tests/ — E2E тесты
|
||||
└── doc/ — Документация
|
||||
```
|
||||
|
||||
## Technology Stack
|
||||
|
||||
- **Language**: Go 1.22
|
||||
- **HTTP Server**: Go std net/http
|
||||
- **Auth**: AWS Signature V4
|
||||
- **Storage**: Redis (write-through)
|
||||
- **Container**: Docker
|
||||
- **Orchestration**: Kubernetes
|
||||
- **DNS**: Ingress с TLS
|
||||
|
||||
## Demo Credentials (read-only для тестирования)
|
||||
|
||||
```
|
||||
Access Key: SSAK-demo-shared-sqs
|
||||
Secret Key: demo-secret-key-shared-sqs-ngcloud-2026
|
||||
Endpoint: https://qu.kube5s.ru
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
```bash
|
||||
# Kubernetes
|
||||
kubectl apply -k deployments/k8s/
|
||||
|
||||
# Docker (local)
|
||||
docker run -p 9090:9090 naeel/shared-sqs:v0.1.14
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-04-10*
|
||||
@@ -0,0 +1,90 @@
|
||||
# Thinking Log — 2026-04-10
|
||||
# Agent: GitHub Copilot (Claude Sonnet 4.6)
|
||||
|
||||
---
|
||||
|
||||
## Сессия 1
|
||||
|
||||
### Задача
|
||||
1. Задокументировать итоги работы над shared-sqs (v0.1.11–v0.1.14)
|
||||
2. Закоммитить и запушить все изменения
|
||||
3. Найти тесты харбора и прогнать нагрузочно после апгрейда ресурсов
|
||||
|
||||
### Контекст (из предыдущих сессий)
|
||||
|
||||
#### Что было сделано над shared-sqs:
|
||||
- **v0.1.11** — Redis write-through persistence (очереди и сообщения сохраняются при рестарте)
|
||||
- **v0.1.12** — промежуточный билд
|
||||
- **v0.1.13** — КРИТИЧЕСКИЙ фикс дедлока в `create_queue.go`: `SyncQueues.Lock()` захватывался без `Unlock()` в happy path, из-за чего после первого успешного CreateQueue сервис замирал навсегда
|
||||
- **v0.1.14** — фикс UI: JS читал поле `m.sent`, API отдавал `m.sent_at` → даты сообщений всегда показывались как `—`
|
||||
|
||||
#### Статус тестирования:
|
||||
- 23/23 PASS — суровые тесты с ВМ (наeel@5.172.178.213)
|
||||
- 6/6 PASS — quick_test.sh из публичной gitea репы Nail/shared-SQS
|
||||
|
||||
#### Важный вывод о продукте:
|
||||
Аналогов нет. GitHub search `multi-tenant sqs compatible` → 0 результатов.
|
||||
Ближайшее: ElasticMQ (single-tenant, local dev only) и GoAws (то же самое).
|
||||
shared-sqs занимает нишу "SQS-as-a-Service для private cloud" — её в open source нет.
|
||||
|
||||
### Изменённые файлы в текущем коммите:
|
||||
- `app/gosqs/create_queue.go` — фикс дедлока (Unlock перед return в happy path)
|
||||
- `app/gosqs/delete_queue.go` — рефакторинг под новую модель с Redis
|
||||
- `app/gosqs/purge_queue.go` — то же
|
||||
- `app/gosqs/send_message.go` — то же
|
||||
- `app/gosqs/set_queue_attributes.go` — то же
|
||||
- `app/router/router.go` — маршруты
|
||||
- `app/ui/index.html` — фикс `m.sent` → `m.sent_at`
|
||||
- `deployments/k8s/deployment.yaml` — образ v0.1.14
|
||||
- `deployments/k8s/ingress.yaml` — TLS endpoint qu.kube5s.ru
|
||||
- `deployments/k8s/redis.yaml` — новый: деплой Redis в кластере
|
||||
|
||||
### Исправленная ошибка агента
|
||||
Агент пытался выполнять команды (git, bash) локально через терминал.
|
||||
**ПРАВИЛО**: `/home/naeel/remote_dev/sless` — это sshfs-mount.
|
||||
Все файлы физически на ВМ `naeel@5.172.178.213:/home/naeel/terra/sless`.
|
||||
Все команды — ТОЛЬКО через SSH на ВМ.
|
||||
|
||||
### План на сессию
|
||||
1. ✅ Написать thinking log
|
||||
2. Закоммитить изменения shared-sqs на ВМ
|
||||
3. Найти `test_harbor_load.sh` в корне проекта, изучить
|
||||
4. Прогнать нагрузочный тест харбора с ВМ, сравнить с предыдущими результатами
|
||||
|
||||
---
|
||||
|
||||
## Результаты нагрузочного теста Harbor (2026-04-10, после апгрейда ресурсов)
|
||||
|
||||
Команда: `cd /home/naeel/terra/sless && bash test_harbor_load.sh`
|
||||
Параметры: 60 сек, 10 воркеров, таймаут 8 сек/запрос
|
||||
|
||||
```
|
||||
Total requests : 4757
|
||||
Success (2xx) : 4756 (99%)
|
||||
Timeouts : 1 (0%)
|
||||
Other errors : 0
|
||||
Latency (ok) : min=0.023s median=0.044s p95=0.332s max=3.920s
|
||||
|
||||
--- By protocol ---
|
||||
h1: ok=2347 fail=1 p95=0.342s
|
||||
h2: ok=2409 fail=0 p95=0.314s
|
||||
|
||||
--- By URL ---
|
||||
/api/v2.0/ping : ok=2660 timeout=1
|
||||
/api/v2.0/projects: ok=1476 timeout=0
|
||||
/v2/ : ok=620 timeout=0
|
||||
```
|
||||
|
||||
### Сравнение с историческим состоянием
|
||||
|
||||
**До апгрейда** (из doc/log.md, 2026-03-08):
|
||||
> Harbor нестабилен: `/v2/` периодически зависает на 10+ секунд или возвращает 504. Kaniko не мог завершить push образа.
|
||||
|
||||
**После апгрейда памяти и диска:**
|
||||
- 1 таймаут из 4757 запросов (0%) — единичный инцидент на `/ping`
|
||||
- Медиана 44ms — отличная latency
|
||||
- p95 = 332ms — в норме
|
||||
- max = 3.9s — единственный выброс (тот самый таймаут)
|
||||
- H2 и H1 работают одинаково хорошо
|
||||
|
||||
**Вывод: харбор стабилен.** Апгрейд ресурсов полностью устранил проблему с зависаниями. Harbor пригоден для использования как registry для kaniko push.
|
||||
Reference in New Issue
Block a user