Ana içeriğe geç

OCPP Charger Create — HTTP 500

POST /api/ocpp/chargers endpoint'i HTTP 500 dönüyorsa (manuel charger oluşturma form'unda "Hata: Internal server error") bu rehberi takip edin. PR #277 ile kök sebep çözüldü; aynı semptom tekrar görünüyorsa regression checklist'i geçin.

Semptom

  • Frontend /chargers/new form submit → "Hata: Internal server error" toast.
  • Network tab'inde POST /api/ocpp/chargers500 Internal Server Error.
  • Diğer OCPP endpoint'leri (GET /api/ocpp/chargers, GET /api/ocpp/chargers/:id) çalışıyor — sadece CREATE endpoint'inde sorun.

Hızlı Tanı

1. Backend log

docker logs zeus-backend 2>&1 | grep -A 20 "create_charger"

Tipik hata trace:

sqlalchemy.exc.IntegrityError: (sqlalchemy.dialects.postgresql.asyncpg.IntegrityError)
<class 'asyncpg.exceptions.CheckViolationError'>:
new row for relation "devices" violates check constraint "ck_devices_chk_device_source"
DETAIL: Failing row contains (..., ocpp, ...).
[SQL: INSERT INTO devices (...) VALUES (...)]

Anahtar mesaj: violates check constraint "ck_devices_chk_device_source".

2. DB tarafında constraint'in mevcut hali

docker exec -it zeus-postgres psql -U postgres -d zeus -c \
"SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname='ck_devices_chk_device_source';"

4 değer (sorunlu):

CHECK ((device_source)::text = ANY (ARRAY['modbus'::text, 'zigbee'::text, 'virtual'::text, 'manual'::text]))

5 değer (çözülmüş):

CHECK ((device_source)::text = ANY (ARRAY['modbus'::text, 'zigbee'::text, 'virtual'::text, 'manual'::text, 'ocpp'::text]))

3. Alembic head'i kontrol

docker exec zeus-backend alembic current
# Beklenen: 0038_chk_device_source_ocpp (head)

Eğer 0037_add_ocpp_tables veya daha eski → migration apply edilmemiş.

Kök Sebep

0037_add_ocpp_tables migration'ı OCPP 1.6J şemasını (8 tablo + hypertable) eklerken devices.chk_device_source CHECK constraint listesine 'ocpp' eklemeyi atladı. Whitelist baseline'dan beri ('modbus', 'zigbee', 'virtual', 'manual') (4 değer).

Backend create_charger (backend/app/features/ocpp/service.py:305) yeni Charge Point kaydı oluştururken:

device = Device(
tenant_id=tenant_id,
name=payload.name,
device_source="ocpp", # ← whitelist'te yok
...
)
session.add(device)
await session.flush() # ← CheckViolationError burada fırlar

Sonuç: INSERT reddedilir, transaction rollback olur, FastAPI 500 döner.

Niye 0037 değil de 0038? 0037 zaten production'da apply edilmişti; düzeltme amaçlı ayrı migration daha güvenli (idempotent + alembic_version forward-only).

Niye CI'da yakalanmadı? Charger create endpoint'i için integration testi yoktu (test gap). PR #277 ile schema-model contract test eklendi (backend/tests/integration/test_schema_model_contract.py) — gelecek incident'leri yakalar.

Çözüm — PR #277

1. 0038_chk_device_source_ocpp migration

backend/alembic/versions/0038_chk_device_source_ocpp.py — NOT VALID + VALIDATE pattern (production lock minimize):

# 1) Eski constraint DROP
ALTER TABLE devices DROP CONSTRAINT ck_devices_chk_device_source;

# 2) Yeni constraint NOT VALID — anlık metadata, satır taraması YOK
ALTER TABLE devices
ADD CONSTRAINT ck_devices_chk_device_source
CHECK (device_source IN ('modbus','zigbee','virtual','manual','ocpp'))
NOT VALID;

# 3) VALIDATE — ShareUpdateExclusiveLock; okuma/yazma trafiği bloklanmaz
ALTER TABLE devices VALIDATE CONSTRAINT ck_devices_chk_device_source;

Detay: Migration Rehberi — NOT VALID + VALIDATE.

2. MIGRATION_TARGET=head deploy.yml fix

.github/workflows/deploy.yml:244MIGRATION_TARGET="${MIGRATION_TARGET:-merge_sofar_mqtt}"head.

Önceden 0037+ revisionlar otomatik deploy'a giremiyordu (workflow_dispatch override veya manuel SSH apply gerektiriyordu). Bu sebeple 0038 migration'ı deploy'a otomatik girebilsin diye varsayılan head'e taşındı. Detay: CI/CD Pipeline.

3. Schema-Model contract test

backend/tests/integration/test_schema_model_contract.py — 7 test, drift detection. App device_source literal'leri ⊆ DB whitelist invariant'ı CI'da doğrulanır.

Doğrulama Komutları

1. Migration apply

docker exec zeus-backend alembic current
# Beklenen: 0038_chk_device_source_ocpp (head)

# Eğer değilse:
docker exec zeus-backend alembic upgrade head

2. Constraint güncel mi

docker exec -it zeus-postgres psql -U postgres -d zeus -c \
"SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname='ck_devices_chk_device_source';"
# Beklenen: ARRAY içinde 'ocpp' var

3. End-to-end smoke (charger create)

# 1. Login → token al
TOKEN=$(curl -s -X POST https://enerji.kepmark.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"...","password":"..."}' | jq -r .access_token)

# 2. Charger create
curl -i -X POST https://enerji.kepmark.com/api/ocpp/chargers \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"cpid": "TEST-CP-01",
"vendor": "ZJ-Beny",
"model": "BCP-AT2N-P",
"serial_number": "SMOKE-001",
"name": "Smoke Test Charger"
}'
# Beklenen: 201 Created + charger objesi (id, cpid, ...)

4. Backend log temiz mi

docker logs zeus-backend --since 1m 2>&1 | grep -E "CheckViolation|create_charger" | head -10
# Beklenen: HİÇBİR CheckViolationError satırı

Regression Koruma

PR #277 ile gelen test_schema_model_contract.py artık drift'i CI'da yakalıyor. Eğer ileride birisi device_source="<yeni-değer>" ekler ama whitelist migration'ını unutursa CI fail eder:

# backend/tests/integration/test_schema_model_contract.py (özet)
def test_device_source_literals_subset_of_db_whitelist():
"""App device_source literal'leri DB whitelist'in alt kümesi olmalı."""
app_literals = collect_device_source_literals_in_app()
db_whitelist = parse_check_constraint_whitelist("ck_devices_chk_device_source")
drift = app_literals - db_whitelist
assert not drift, f"Schema-model drift: {drift} app'te var ama DB whitelist'inde yok"

Tekrar Görürsen Checklist

AdımKomutBeklenen
Migration apply edildi mi?alembic current0038_chk_device_source_ocpp
MIGRATION_TARGET head mi?cat deploy.sh | grep MIGRATION_TARGET${MIGRATION_TARGET:-head}
Constraint güncel mi?DB pg_constraint sorgusu'ocpp' whitelist'te
Contract test pass mi?pytest -m "not integration" tests/integration/test_schema_model_contract.pyyeşil
Backend log temiz mi?docker logs zeus-backend | grep CheckViolationhiç satır yok

Bilinen Issue (Follow-up)

alembic_version.version_num kolonu VARCHAR(32). İlk denememizde 0038_extend_chk_device_source_ocpp (34 char) StringDataRightTruncationError verdi → 0038_chk_device_source_ocpp (27 char) ile düzeltildi.

Follow-up: alembic_version VARCHAR(64) migration ayrı PR. Geçici workaround: revision id 32 char altında tut.

İlgili Dokümanlar