Compare commits
4 Commits
2dda1aff00
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7860adbaa | ||
|
|
a71b3f36ec | ||
|
|
5b0d2f78d6 | ||
|
|
3f5b93abdd |
@@ -5,16 +5,29 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: scaev
|
container_name: scaev
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
# Voeg het PostgreSQL-netwerk toe
|
||||||
networks:
|
networks:
|
||||||
scaev_mobile_net:
|
scaev_mobile_net:
|
||||||
ipv4_address: 172.30.0.10
|
ipv4_address: 172.30.0.10
|
||||||
traefik_net:
|
traefik_net:
|
||||||
|
db_net:
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
SCAEV_OFFLINE: 0
|
||||||
RATE_LIMIT_SECONDS: "0.5"
|
RATE_LIMIT_SECONDS: "0.5"
|
||||||
MAX_PAGES: "500"
|
MAX_PAGES: "500"
|
||||||
DOWNLOAD_IMAGES: "True"
|
DOWNLOAD_IMAGES: "True"
|
||||||
|
|
||||||
|
# Nieuw: verbind intern via service-naam, niet via LAN IP
|
||||||
|
POSTGRES_HOST: postgres
|
||||||
|
POSTGRES_DB: auctiondb
|
||||||
|
POSTGRES_USER: auction
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
- shared-auction-data:/mnt/okcomputer/output
|
- shared-auction-data:/mnt/okcomputer/output
|
||||||
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.scaev.rule=Host(`scaev.appmodel.nl`)"
|
- "traefik.http.routers.scaev.rule=Host(`scaev.appmodel.nl`)"
|
||||||
@@ -23,7 +36,6 @@ services:
|
|||||||
- "traefik.http.routers.scaev.tls.certresolver=letsencrypt"
|
- "traefik.http.routers.scaev.tls.certresolver=letsencrypt"
|
||||||
- "traefik.http.services.scaev.loadbalancer.server.port=8000"
|
- "traefik.http.services.scaev.loadbalancer.server.port=8000"
|
||||||
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
scaev_mobile_net:
|
scaev_mobile_net:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
@@ -33,10 +45,16 @@ networks:
|
|||||||
config:
|
config:
|
||||||
- subnet: 172.30.0.0/24
|
- subnet: 172.30.0.0/24
|
||||||
gateway: 172.30.0.1
|
gateway: 172.30.0.1
|
||||||
|
|
||||||
traefik_net:
|
traefik_net:
|
||||||
external: true
|
external: true
|
||||||
name: traefik_net
|
name: traefik_net
|
||||||
|
|
||||||
|
# Nieuw: gedeeld netwerk voor scaev en postgres
|
||||||
|
db_net:
|
||||||
|
external: true
|
||||||
|
name: db_net
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
shared-auction-data:
|
shared-auction-data:
|
||||||
external: true
|
external: true
|
||||||
|
|||||||
85
src/cache.py
85
src/cache.py
@@ -15,6 +15,27 @@ import json
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
def _mask_dsn(dsn: str) -> str:
|
||||||
|
try:
|
||||||
|
p = urlparse(dsn)
|
||||||
|
user = p.username or ''
|
||||||
|
host = p.hostname or ''
|
||||||
|
port = f":{p.port}" if p.port else ''
|
||||||
|
return f"{p.scheme}://{user}:***@{host}{port}{p.path or ''}"
|
||||||
|
except Exception:
|
||||||
|
return dsn
|
||||||
|
|
||||||
|
def _describe_target(dsn: str) -> str:
|
||||||
|
try:
|
||||||
|
p = urlparse(dsn)
|
||||||
|
host = p.hostname or 'unknown-host'
|
||||||
|
port = p.port or 5432
|
||||||
|
db = (p.path or '').lstrip('/') or '<no-db>'
|
||||||
|
return f"{host}:{port}/{db}"
|
||||||
|
except Exception:
|
||||||
|
return dsn
|
||||||
|
|
||||||
|
|
||||||
class _ConnectionPool:
|
class _ConnectionPool:
|
||||||
@@ -36,6 +57,12 @@ class _ConnectionPool:
|
|||||||
self._idle: list = []
|
self._idle: list = []
|
||||||
self._created = 0
|
self._created = 0
|
||||||
|
|
||||||
|
# Basic diagnostics
|
||||||
|
try:
|
||||||
|
print(f"[DB] Initializing connection pool: min={self._min} max={self._max} wait_timeout={self._timeout}s target={_describe_target(self._dsn)}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Pre-warm pool
|
# Pre-warm pool
|
||||||
for _ in range(self._min):
|
for _ in range(self._min):
|
||||||
conn = self._new_connection_with_retry()
|
conn = self._new_connection_with_retry()
|
||||||
@@ -45,17 +72,43 @@ class _ConnectionPool:
|
|||||||
def _new_connection_with_retry(self):
|
def _new_connection_with_retry(self):
|
||||||
last_exc = None
|
last_exc = None
|
||||||
backoffs = [0.05, 0.1, 0.2, 0.4, 0.8]
|
backoffs = [0.05, 0.1, 0.2, 0.4, 0.8]
|
||||||
|
target = _describe_target(self._dsn)
|
||||||
|
attempt = 0
|
||||||
for delay in backoffs:
|
for delay in backoffs:
|
||||||
|
attempt += 1
|
||||||
try:
|
try:
|
||||||
return self._connect(self._dsn)
|
t0 = time.time()
|
||||||
|
conn = self._connect(self._dsn)
|
||||||
|
dt = time.time() - t0
|
||||||
|
try:
|
||||||
|
print(f"[DB] Connected to {target} on attempt {attempt} in {dt:.2f}s")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return conn
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
last_exc = e
|
last_exc = e
|
||||||
|
try:
|
||||||
|
print(f"[DB] Connect attempt {attempt} to {target} failed: {type(e).__name__}: {e}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
# Final attempt without sleeping after loop
|
# Final attempt without sleeping after loop
|
||||||
try:
|
try:
|
||||||
return self._connect(self._dsn)
|
attempt += 1
|
||||||
|
t0 = time.time()
|
||||||
|
conn = self._connect(self._dsn)
|
||||||
|
dt = time.time() - t0
|
||||||
|
try:
|
||||||
|
print(f"[DB] Connected to {target} on attempt {attempt} in {dt:.2f}s")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return conn
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
last_exc = e
|
last_exc = e
|
||||||
|
try:
|
||||||
|
print(f"[DB] Final connect attempt {attempt} to {target} failed: {type(e).__name__}: {e}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
raise last_exc
|
raise last_exc
|
||||||
|
|
||||||
def acquire(self, timeout: Optional[float] = None):
|
def acquire(self, timeout: Optional[float] = None):
|
||||||
@@ -68,11 +121,19 @@ class _ConnectionPool:
|
|||||||
try:
|
try:
|
||||||
if getattr(conn, "closed", False):
|
if getattr(conn, "closed", False):
|
||||||
self._created -= 1
|
self._created -= 1
|
||||||
|
try:
|
||||||
|
print("[DB] Dropping closed connection from pool; adjusting pool size")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
continue
|
continue
|
||||||
return conn
|
return conn
|
||||||
except Exception:
|
except Exception:
|
||||||
# Consider it broken
|
# Consider it broken
|
||||||
self._created -= 1
|
self._created -= 1
|
||||||
|
try:
|
||||||
|
print("[DB] Encountered error while checking pooled connection; dropping it")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Create new if capacity
|
# Create new if capacity
|
||||||
@@ -84,7 +145,15 @@ class _ConnectionPool:
|
|||||||
# Wait for release
|
# Wait for release
|
||||||
remaining = deadline - time.time()
|
remaining = deadline - time.time()
|
||||||
if remaining <= 0:
|
if remaining <= 0:
|
||||||
|
try:
|
||||||
|
print(f"[DB] Timed out after {self._timeout}s waiting for a pooled connection (in use: {self._created}/{self._max})")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
raise TimeoutError("Timed out waiting for database connection from pool")
|
raise TimeoutError("Timed out waiting for database connection from pool")
|
||||||
|
try:
|
||||||
|
print(f"[DB] Pool exhausted ({self._created}/{self._max}); waiting up to {remaining:.1f}s for release")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
self._cond.wait(remaining)
|
self._cond.wait(remaining)
|
||||||
|
|
||||||
def release(self, conn):
|
def release(self, conn):
|
||||||
@@ -96,6 +165,10 @@ class _ConnectionPool:
|
|||||||
if getattr(conn, "closed", False):
|
if getattr(conn, "closed", False):
|
||||||
with self._cond:
|
with self._cond:
|
||||||
self._created -= 1
|
self._created -= 1
|
||||||
|
try:
|
||||||
|
print("[DB] Released connection was closed; decrementing pool size")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
self._cond.notify()
|
self._cond.notify()
|
||||||
return
|
return
|
||||||
with self._cond:
|
with self._cond:
|
||||||
@@ -127,6 +200,10 @@ class _ConnectionPool:
|
|||||||
pass
|
pass
|
||||||
self._idle.clear()
|
self._idle.clear()
|
||||||
self._created = 0
|
self._created = 0
|
||||||
|
try:
|
||||||
|
print("[DB] Connection pool closed; all idle connections terminated")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
class CacheManager:
|
class CacheManager:
|
||||||
"""Manages page caching and data storage using PostgreSQL."""
|
"""Manages page caching and data storage using PostgreSQL."""
|
||||||
@@ -142,6 +219,10 @@ class CacheManager:
|
|||||||
max_size=getattr(config, 'DB_POOL_MAX', 6),
|
max_size=getattr(config, 'DB_POOL_MAX', 6),
|
||||||
timeout=getattr(config, 'DB_POOL_TIMEOUT', 30),
|
timeout=getattr(config, 'DB_POOL_TIMEOUT', 30),
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
print(f"[DB] CacheManager initialized with DSN={_mask_dsn(self.database_url)}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
self._init_db()
|
self._init_db()
|
||||||
|
|
||||||
# ------------------------
|
# ------------------------
|
||||||
|
|||||||
@@ -15,15 +15,24 @@ if sys.version_info < (3, 10):
|
|||||||
|
|
||||||
# ==================== CONFIGURATION ====================
|
# ==================== CONFIGURATION ====================
|
||||||
BASE_URL = "https://www.troostwijkauctions.com"
|
BASE_URL = "https://www.troostwijkauctions.com"
|
||||||
|
POSTGRES_HOST = os.getenv("POSTGRES_HOST", "postgres")
|
||||||
|
POSTGRES_DB = os.getenv("POSTGRES_DB", "auctiondb")
|
||||||
|
POSTGRES_USER = os.getenv("POSTGRES_USER", "auction")
|
||||||
|
POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD", "heel-goed-wachtwoord")
|
||||||
|
# Full DSN
|
||||||
|
DATABASE_URL = os.getenv(
|
||||||
|
"DATABASE_URL",
|
||||||
|
f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:5432/{POSTGRES_DB}"
|
||||||
|
).strip()
|
||||||
|
|
||||||
# Primary database: PostgreSQL only
|
# Primary database: PostgreSQL only
|
||||||
# Override via environment variable DATABASE_URL
|
# Override via environment variable DATABASE_URL
|
||||||
# Example: postgresql://user:pass@host:5432/dbname
|
# Example: postgresql://user:pass@host:5432/dbname
|
||||||
DATABASE_URL = os.getenv(
|
# DATABASE_URL = os.getenv(
|
||||||
"DATABASE_URL",
|
# "DATABASE_URL",
|
||||||
# Default provided by ops
|
# # Default provided by ops
|
||||||
"postgresql://auction:heel-goed-wachtwoord@192.168.1.159:5432/auctiondb",
|
# "postgresql://auction:heel-goed-wachtwoord@192.168.1.159:5432/auctiondb",
|
||||||
).strip()
|
# ).strip()
|
||||||
|
|
||||||
# Database connection pool controls (to avoid creating too many short-lived TCP connections)
|
# Database connection pool controls (to avoid creating too many short-lived TCP connections)
|
||||||
# Environment overrides: SCAEV_DB_POOL_MIN, SCAEV_DB_POOL_MAX, SCAEV_DB_POOL_TIMEOUT
|
# Environment overrides: SCAEV_DB_POOL_MIN, SCAEV_DB_POOL_MAX, SCAEV_DB_POOL_TIMEOUT
|
||||||
|
|||||||
Reference in New Issue
Block a user