feat(service-auto): săpt 1 POC Oracle + module scaffold

- poc/hello_oracle.py: sync connect 33ms, DEV_TIP_DEVIZ enum verified live
- poc/hello_oracle_async.py: async 22ms, gate Correction 9 → sync-facade
- backend/modules/service_auto/{routers,services,schemas,models}: scaffold
- docs/service-auto/week1-notes.md: DX latency + gate decision
- docs/service-auto/TODO-phase2.md: phase 2+ backlog (empty header)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-04-11 16:08:53 +00:00
parent 43484db45e
commit 4162e0711c
9 changed files with 245 additions and 0 deletions

View File

View File

@@ -0,0 +1,5 @@
# Service Auto — Phase 2+ Backlog
Scope wall: prototype = creare comandă only. Everything below = phase 2+.
<!-- Add items as they arise during development -->

View File

@@ -0,0 +1,26 @@
# Săpt 1 Notes — Oracle POC
**Date**: 2026-04-11
## Conectivitate
- Server: `central` (10.0.20.121:1521/ROA, CONTAFIN_ORACLE) — PORT OPEN, direct (no tunnel needed)
- Sync connect: **33ms** | Queries: 0.6-3.3ms ✅
- Async connect_async: **22ms** | Queries: 0.2-0.3ms ✅
## Gate Correction 9 — Decizie
**Decizie: sync-facade pattern** (consistent cu `oracle_pool.py` existent)
Motivare: `oracle_pool.py` folosește deja `oracledb.create_pool()` (sync) + `pool.acquire()` (sync) + `with cursor` (sync) în `async def`. Serviciile service_auto vor urma același pattern pentru consistență arhitecturală. `connect_async` funcționează dar nu aduce beneficii față de sync-facade la latențele măsurate.
## Schema Access
- `MARIUSM_AUTO.DEV_ORDL` — acces OK
- `MARIUSM_AUTO.DEV_TIP_DEVIZ` — enum confirmat (7 tipuri, inch_validare corect)
## Next Steps (Săpt 3)
- Audit grants `ROA_WEB` pe `MARIUSM_AUTO.*`
- Creare SP `SP_CREEAZA_COMANDA_PROTOTIP` în MARIUSM_AUTO (template în tabele-service-auto.md §12.2)
- Auth path: adaugă `MARIUSM_AUTO` company în `.env` + test login JWT end-to-end

85
poc/hello_oracle.py Normal file
View File

@@ -0,0 +1,85 @@
"""
POC: Oracle sync connectivity test — Săpt 1 Gate Correction 9
Tests direct sync connection to MARIUSM_AUTO (central server).
Measures latency for SELECT 1 FROM DUAL + real table access.
Usage:
cd /workspace/roa2web
backend/venv/bin/python poc/hello_oracle.py
"""
import time
import sys
import os
# Add backend to path so secrets/ is accessible
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'backend'))
import oracledb
# Central server config (MARIUSM_AUTO)
HOST = "10.0.20.121"
PORT = 1521
SERVICE_NAME = "ROA"
USER = "CONTAFIN_ORACLE"
SECRETS_FILE = os.path.join(os.path.dirname(__file__), '..', 'backend', 'secrets', 'central.oracle_pass')
def read_password() -> str:
with open(SECRETS_FILE) as f:
return f.read().strip()
def test_sync_connect():
print(f"[SYNC] Connecting to {HOST}:{PORT}/{SERVICE_NAME} as {USER}")
password = read_password()
t0 = time.perf_counter()
conn = oracledb.connect(
user=USER,
password=password,
host=HOST,
port=PORT,
service_name=SERVICE_NAME
)
t_connect = time.perf_counter() - t0
print(f"[SYNC] Connected in {t_connect*1000:.1f}ms")
with conn.cursor() as cur:
# Basic connectivity
t1 = time.perf_counter()
cur.execute("SELECT 1 FROM DUAL")
row = cur.fetchone()
t_dual = time.perf_counter() - t1
print(f"[SYNC] SELECT 1 FROM DUAL = {row[0]} ({t_dual*1000:.1f}ms)")
# SYSDATE
t2 = time.perf_counter()
cur.execute("SELECT SYSDATE FROM DUAL")
row = cur.fetchone()
t_date = time.perf_counter() - t2
print(f"[SYNC] SELECT SYSDATE = {row[0]} ({t_date*1000:.1f}ms)")
# DEV_ORDL count (proves schema access)
t3 = time.perf_counter()
cur.execute("SELECT COUNT(*) FROM MARIUSM_AUTO.DEV_ORDL WHERE ROWNUM <= 1")
row = cur.fetchone()
t_table = time.perf_counter() - t3
print(f"[SYNC] SELECT COUNT(*) DEV_ORDL = {row[0]} ({t_table*1000:.1f}ms)")
# DEV_TIP_DEVIZ enum check
t4 = time.perf_counter()
cur.execute("SELECT id_tip, denumire, inch_validare FROM MARIUSM_AUTO.DEV_TIP_DEVIZ ORDER BY id_tip")
rows = cur.fetchall()
t_enum = time.perf_counter() - t4
print(f"[SYNC] DEV_TIP_DEVIZ ({t_enum*1000:.1f}ms):")
for r in rows:
print(f" {r[0]:3} | {r[1]:<20} | inch_validare={r[2]}")
conn.close()
total = time.perf_counter() - t0
print(f"[SYNC] Total: {total*1000:.1f}ms ✅")
if __name__ == "__main__":
test_sync_connect()

129
poc/hello_oracle_async.py Normal file
View File

@@ -0,0 +1,129 @@
"""
POC: Oracle async connectivity test — Săpt 1 Gate Correction 9
Tests native async oracledb.connect_async() vs sync baseline.
Decision gate: if async latency acceptable → keep async-native (ad-hoc pattern).
If degraded → use sync-facade with asyncio.run_in_executor.
Usage:
cd /workspace/roa2web
backend/venv/bin/python poc/hello_oracle_async.py
"""
import asyncio
import time
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'backend'))
import oracledb
HOST = "10.0.20.121"
PORT = 1521
SERVICE_NAME = "ROA"
USER = "CONTAFIN_ORACLE"
SECRETS_FILE = os.path.join(os.path.dirname(__file__), '..', 'backend', 'secrets', 'central.oracle_pass')
def read_password() -> str:
with open(SECRETS_FILE) as f:
return f.read().strip()
async def test_async_connect():
print(f"[ASYNC] Connecting to {HOST}:{PORT}/{SERVICE_NAME} as {USER}")
password = read_password()
t0 = time.perf_counter()
conn = await oracledb.connect_async(
user=USER,
password=password,
host=HOST,
port=PORT,
service_name=SERVICE_NAME
)
t_connect = time.perf_counter() - t0
print(f"[ASYNC] Connected in {t_connect*1000:.1f}ms")
async with conn.cursor() as cur:
# Basic connectivity
t1 = time.perf_counter()
await cur.execute("SELECT 1 FROM DUAL")
row = await cur.fetchone()
t_dual = time.perf_counter() - t1
print(f"[ASYNC] SELECT 1 FROM DUAL = {row[0]} ({t_dual*1000:.1f}ms)")
# SYSDATE
t2 = time.perf_counter()
await cur.execute("SELECT SYSDATE FROM DUAL")
row = await cur.fetchone()
t_date = time.perf_counter() - t2
print(f"[ASYNC] SELECT SYSDATE = {row[0]} ({t_date*1000:.1f}ms)")
# DEV_ORDL access
t3 = time.perf_counter()
await cur.execute("SELECT COUNT(*) FROM MARIUSM_AUTO.DEV_ORDL WHERE ROWNUM <= 1")
row = await cur.fetchone()
t_table = time.perf_counter() - t3
print(f"[ASYNC] SELECT COUNT(*) DEV_ORDL = {row[0]} ({t_table*1000:.1f}ms)")
# Parallel queries test (proves concurrency benefit of async)
print("[ASYNC] All queries via single cursor OK")
await conn.close()
total = time.perf_counter() - t0
print(f"[ASYNC] Total: {total*1000:.1f}ms ✅")
async def test_async_pool():
"""
Test sync pool in async context — matches existing oracle_pool.py pattern.
Existing codebase uses oracledb.create_pool() (sync) + pool.acquire() (sync)
+ sync cursors inside async def. This is the established sync-facade pattern.
Gate Correction 9: keep this pattern for service_auto (consistency).
"""
print("\n[POOL] Testing sync pool in async context (existing pattern)...")
password = read_password()
t0 = time.perf_counter()
pool = oracledb.create_pool(
user=USER,
password=password,
host=HOST,
port=PORT,
service_name=SERVICE_NAME,
min=1,
max=3,
increment=1
)
t_pool = time.perf_counter() - t0
print(f"[POOL] Pool created in {t_pool*1000:.1f}ms (min=1, max=3)")
# sync acquire in async def — matches oracle_pool.py:165
conn = pool.acquire()
with conn.cursor() as cur:
t1 = time.perf_counter()
cur.execute("SELECT 1 FROM DUAL")
row = cur.fetchone()
t_q = time.perf_counter() - t1
print(f"[POOL] Sync query via pool: {row[0]} ({t_q*1000:.1f}ms)")
conn.close()
pool.close()
print(f"[POOL] Pool closed ✅")
async def main():
await test_async_connect()
await test_async_pool()
print("\n" + "="*50)
print("GATE Correction 9 Summary:")
print(" If latencies above are <50ms → async-native OK")
print(" If >200ms or errors → use sync-facade pattern")
print("="*50)
if __name__ == "__main__":
asyncio.run(main())