truthound-dashboard 1.3.1__py3-none-any.whl → 1.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truthound_dashboard/api/alerts.py +258 -0
- truthound_dashboard/api/anomaly.py +1302 -0
- truthound_dashboard/api/cross_alerts.py +352 -0
- truthound_dashboard/api/deps.py +143 -0
- truthound_dashboard/api/drift_monitor.py +540 -0
- truthound_dashboard/api/lineage.py +1151 -0
- truthound_dashboard/api/maintenance.py +363 -0
- truthound_dashboard/api/middleware.py +373 -1
- truthound_dashboard/api/model_monitoring.py +805 -0
- truthound_dashboard/api/notifications_advanced.py +2452 -0
- truthound_dashboard/api/plugins.py +2096 -0
- truthound_dashboard/api/profile.py +211 -14
- truthound_dashboard/api/reports.py +853 -0
- truthound_dashboard/api/router.py +147 -0
- truthound_dashboard/api/rule_suggestions.py +310 -0
- truthound_dashboard/api/schema_evolution.py +231 -0
- truthound_dashboard/api/sources.py +47 -3
- truthound_dashboard/api/triggers.py +190 -0
- truthound_dashboard/api/validations.py +13 -0
- truthound_dashboard/api/validators.py +333 -4
- truthound_dashboard/api/versioning.py +309 -0
- truthound_dashboard/api/websocket.py +301 -0
- truthound_dashboard/core/__init__.py +27 -0
- truthound_dashboard/core/anomaly.py +1395 -0
- truthound_dashboard/core/anomaly_explainer.py +633 -0
- truthound_dashboard/core/cache.py +206 -0
- truthound_dashboard/core/cached_services.py +422 -0
- truthound_dashboard/core/charts.py +352 -0
- truthound_dashboard/core/connections.py +1069 -42
- truthound_dashboard/core/cross_alerts.py +837 -0
- truthound_dashboard/core/drift_monitor.py +1477 -0
- truthound_dashboard/core/drift_sampling.py +669 -0
- truthound_dashboard/core/i18n/__init__.py +42 -0
- truthound_dashboard/core/i18n/detector.py +173 -0
- truthound_dashboard/core/i18n/messages.py +564 -0
- truthound_dashboard/core/lineage.py +971 -0
- truthound_dashboard/core/maintenance.py +443 -5
- truthound_dashboard/core/model_monitoring.py +1043 -0
- truthound_dashboard/core/notifications/channels.py +1020 -1
- truthound_dashboard/core/notifications/deduplication/__init__.py +143 -0
- truthound_dashboard/core/notifications/deduplication/policies.py +274 -0
- truthound_dashboard/core/notifications/deduplication/service.py +400 -0
- truthound_dashboard/core/notifications/deduplication/stores.py +2365 -0
- truthound_dashboard/core/notifications/deduplication/strategies.py +422 -0
- truthound_dashboard/core/notifications/dispatcher.py +43 -0
- truthound_dashboard/core/notifications/escalation/__init__.py +149 -0
- truthound_dashboard/core/notifications/escalation/backends.py +1384 -0
- truthound_dashboard/core/notifications/escalation/engine.py +429 -0
- truthound_dashboard/core/notifications/escalation/models.py +336 -0
- truthound_dashboard/core/notifications/escalation/scheduler.py +1187 -0
- truthound_dashboard/core/notifications/escalation/state_machine.py +330 -0
- truthound_dashboard/core/notifications/escalation/stores.py +2896 -0
- truthound_dashboard/core/notifications/events.py +49 -0
- truthound_dashboard/core/notifications/metrics/__init__.py +115 -0
- truthound_dashboard/core/notifications/metrics/base.py +528 -0
- truthound_dashboard/core/notifications/metrics/collectors.py +583 -0
- truthound_dashboard/core/notifications/routing/__init__.py +169 -0
- truthound_dashboard/core/notifications/routing/combinators.py +184 -0
- truthound_dashboard/core/notifications/routing/config.py +375 -0
- truthound_dashboard/core/notifications/routing/config_parser.py +867 -0
- truthound_dashboard/core/notifications/routing/engine.py +382 -0
- truthound_dashboard/core/notifications/routing/expression_engine.py +1269 -0
- truthound_dashboard/core/notifications/routing/jinja2_engine.py +774 -0
- truthound_dashboard/core/notifications/routing/rules.py +625 -0
- truthound_dashboard/core/notifications/routing/validator.py +678 -0
- truthound_dashboard/core/notifications/service.py +2 -0
- truthound_dashboard/core/notifications/stats_aggregator.py +850 -0
- truthound_dashboard/core/notifications/throttling/__init__.py +83 -0
- truthound_dashboard/core/notifications/throttling/builder.py +311 -0
- truthound_dashboard/core/notifications/throttling/stores.py +1859 -0
- truthound_dashboard/core/notifications/throttling/throttlers.py +633 -0
- truthound_dashboard/core/openlineage.py +1028 -0
- truthound_dashboard/core/plugins/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/extractor.py +703 -0
- truthound_dashboard/core/plugins/docs/renderers.py +804 -0
- truthound_dashboard/core/plugins/hooks/__init__.py +63 -0
- truthound_dashboard/core/plugins/hooks/decorators.py +367 -0
- truthound_dashboard/core/plugins/hooks/manager.py +403 -0
- truthound_dashboard/core/plugins/hooks/protocols.py +265 -0
- truthound_dashboard/core/plugins/lifecycle/__init__.py +41 -0
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +584 -0
- truthound_dashboard/core/plugins/lifecycle/machine.py +419 -0
- truthound_dashboard/core/plugins/lifecycle/states.py +266 -0
- truthound_dashboard/core/plugins/loader.py +504 -0
- truthound_dashboard/core/plugins/registry.py +810 -0
- truthound_dashboard/core/plugins/reporter_executor.py +588 -0
- truthound_dashboard/core/plugins/sandbox/__init__.py +59 -0
- truthound_dashboard/core/plugins/sandbox/code_validator.py +243 -0
- truthound_dashboard/core/plugins/sandbox/engines.py +770 -0
- truthound_dashboard/core/plugins/sandbox/protocols.py +194 -0
- truthound_dashboard/core/plugins/sandbox.py +617 -0
- truthound_dashboard/core/plugins/security/__init__.py +68 -0
- truthound_dashboard/core/plugins/security/analyzer.py +535 -0
- truthound_dashboard/core/plugins/security/policies.py +311 -0
- truthound_dashboard/core/plugins/security/protocols.py +296 -0
- truthound_dashboard/core/plugins/security/signing.py +842 -0
- truthound_dashboard/core/plugins/security.py +446 -0
- truthound_dashboard/core/plugins/validator_executor.py +401 -0
- truthound_dashboard/core/plugins/versioning/__init__.py +51 -0
- truthound_dashboard/core/plugins/versioning/constraints.py +377 -0
- truthound_dashboard/core/plugins/versioning/dependencies.py +541 -0
- truthound_dashboard/core/plugins/versioning/semver.py +266 -0
- truthound_dashboard/core/profile_comparison.py +601 -0
- truthound_dashboard/core/report_history.py +570 -0
- truthound_dashboard/core/reporters/__init__.py +57 -0
- truthound_dashboard/core/reporters/base.py +296 -0
- truthound_dashboard/core/reporters/csv_reporter.py +155 -0
- truthound_dashboard/core/reporters/html_reporter.py +598 -0
- truthound_dashboard/core/reporters/i18n/__init__.py +65 -0
- truthound_dashboard/core/reporters/i18n/base.py +494 -0
- truthound_dashboard/core/reporters/i18n/catalogs.py +930 -0
- truthound_dashboard/core/reporters/json_reporter.py +160 -0
- truthound_dashboard/core/reporters/junit_reporter.py +233 -0
- truthound_dashboard/core/reporters/markdown_reporter.py +207 -0
- truthound_dashboard/core/reporters/pdf_reporter.py +209 -0
- truthound_dashboard/core/reporters/registry.py +272 -0
- truthound_dashboard/core/rule_generator.py +2088 -0
- truthound_dashboard/core/scheduler.py +822 -12
- truthound_dashboard/core/schema_evolution.py +858 -0
- truthound_dashboard/core/services.py +152 -9
- truthound_dashboard/core/statistics.py +718 -0
- truthound_dashboard/core/streaming_anomaly.py +883 -0
- truthound_dashboard/core/triggers/__init__.py +45 -0
- truthound_dashboard/core/triggers/base.py +226 -0
- truthound_dashboard/core/triggers/evaluators.py +609 -0
- truthound_dashboard/core/triggers/factory.py +363 -0
- truthound_dashboard/core/unified_alerts.py +870 -0
- truthound_dashboard/core/validation_limits.py +509 -0
- truthound_dashboard/core/versioning.py +709 -0
- truthound_dashboard/core/websocket/__init__.py +59 -0
- truthound_dashboard/core/websocket/manager.py +512 -0
- truthound_dashboard/core/websocket/messages.py +130 -0
- truthound_dashboard/db/__init__.py +30 -0
- truthound_dashboard/db/models.py +3375 -3
- truthound_dashboard/main.py +22 -0
- truthound_dashboard/schemas/__init__.py +396 -1
- truthound_dashboard/schemas/anomaly.py +1258 -0
- truthound_dashboard/schemas/base.py +4 -0
- truthound_dashboard/schemas/cross_alerts.py +334 -0
- truthound_dashboard/schemas/drift_monitor.py +890 -0
- truthound_dashboard/schemas/lineage.py +428 -0
- truthound_dashboard/schemas/maintenance.py +154 -0
- truthound_dashboard/schemas/model_monitoring.py +374 -0
- truthound_dashboard/schemas/notifications_advanced.py +1363 -0
- truthound_dashboard/schemas/openlineage.py +704 -0
- truthound_dashboard/schemas/plugins.py +1293 -0
- truthound_dashboard/schemas/profile.py +420 -34
- truthound_dashboard/schemas/profile_comparison.py +242 -0
- truthound_dashboard/schemas/reports.py +285 -0
- truthound_dashboard/schemas/rule_suggestion.py +434 -0
- truthound_dashboard/schemas/schema_evolution.py +164 -0
- truthound_dashboard/schemas/source.py +117 -2
- truthound_dashboard/schemas/triggers.py +511 -0
- truthound_dashboard/schemas/unified_alerts.py +223 -0
- truthound_dashboard/schemas/validation.py +25 -1
- truthound_dashboard/schemas/validators/__init__.py +11 -0
- truthound_dashboard/schemas/validators/base.py +151 -0
- truthound_dashboard/schemas/versioning.py +152 -0
- truthound_dashboard/static/index.html +2 -2
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/METADATA +147 -23
- truthound_dashboard-1.4.1.dist-info/RECORD +239 -0
- truthound_dashboard/static/assets/index-BZG20KuF.js +0 -586
- truthound_dashboard/static/assets/index-D_HyZ3pb.css +0 -1
- truthound_dashboard/static/assets/unmerged_dictionaries-CtpqQBm0.js +0 -1
- truthound_dashboard-1.3.1.dist-info/RECORD +0 -110
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.3.1.dist-info → truthound_dashboard-1.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
"""Maintenance API endpoints.
|
|
2
|
+
|
|
3
|
+
This module provides endpoints for database maintenance, retention policies,
|
|
4
|
+
and cache management.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Annotated, Any
|
|
12
|
+
|
|
13
|
+
from fastapi import APIRouter, HTTPException, Query
|
|
14
|
+
|
|
15
|
+
from truthound_dashboard.core.cache import get_cache, get_cache_manager
|
|
16
|
+
from truthound_dashboard.core.maintenance import (
|
|
17
|
+
MaintenanceConfig,
|
|
18
|
+
get_maintenance_manager,
|
|
19
|
+
)
|
|
20
|
+
from truthound_dashboard.core.scheduler import get_scheduler
|
|
21
|
+
from truthound_dashboard.schemas import (
|
|
22
|
+
CacheClearRequest,
|
|
23
|
+
CacheStatsResponse,
|
|
24
|
+
CleanupResultItem,
|
|
25
|
+
CleanupTriggerRequest,
|
|
26
|
+
MaintenanceReportResponse,
|
|
27
|
+
MaintenanceStatusResponse,
|
|
28
|
+
RetentionPolicyConfig,
|
|
29
|
+
RetentionPolicyResponse,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
router = APIRouter()
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# Track last maintenance run (in production, this would be persisted)
|
|
36
|
+
_last_maintenance_run: datetime | None = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@router.get(
|
|
40
|
+
"/retention",
|
|
41
|
+
response_model=RetentionPolicyResponse,
|
|
42
|
+
summary="Get retention policy",
|
|
43
|
+
description="Get current data retention policy configuration",
|
|
44
|
+
)
|
|
45
|
+
async def get_retention_policy() -> RetentionPolicyResponse:
|
|
46
|
+
"""Get current retention policy configuration.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Current retention policy settings.
|
|
50
|
+
"""
|
|
51
|
+
manager = get_maintenance_manager()
|
|
52
|
+
config = manager.config
|
|
53
|
+
|
|
54
|
+
return RetentionPolicyResponse(
|
|
55
|
+
validation_retention_days=config.validation_retention_days,
|
|
56
|
+
profile_keep_per_source=config.profile_keep_per_source,
|
|
57
|
+
notification_log_retention_days=config.notification_log_retention_days,
|
|
58
|
+
run_vacuum=config.run_vacuum,
|
|
59
|
+
enabled=config.enabled,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@router.put(
|
|
64
|
+
"/retention",
|
|
65
|
+
response_model=RetentionPolicyResponse,
|
|
66
|
+
summary="Update retention policy",
|
|
67
|
+
description="Update data retention policy configuration",
|
|
68
|
+
)
|
|
69
|
+
async def update_retention_policy(
|
|
70
|
+
config: RetentionPolicyConfig,
|
|
71
|
+
) -> RetentionPolicyResponse:
|
|
72
|
+
"""Update retention policy configuration.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
config: New retention policy settings.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Updated retention policy.
|
|
79
|
+
"""
|
|
80
|
+
manager = get_maintenance_manager()
|
|
81
|
+
|
|
82
|
+
# Update configuration
|
|
83
|
+
manager._config = MaintenanceConfig(
|
|
84
|
+
validation_retention_days=config.validation_retention_days,
|
|
85
|
+
profile_keep_per_source=config.profile_keep_per_source,
|
|
86
|
+
notification_log_retention_days=config.notification_log_retention_days,
|
|
87
|
+
run_vacuum=config.run_vacuum,
|
|
88
|
+
enabled=config.enabled,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
logger.info(
|
|
92
|
+
f"Retention policy updated: "
|
|
93
|
+
f"validation={config.validation_retention_days}d, "
|
|
94
|
+
f"profiles={config.profile_keep_per_source}/source, "
|
|
95
|
+
f"notifications={config.notification_log_retention_days}d"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return RetentionPolicyResponse(
|
|
99
|
+
validation_retention_days=config.validation_retention_days,
|
|
100
|
+
profile_keep_per_source=config.profile_keep_per_source,
|
|
101
|
+
notification_log_retention_days=config.notification_log_retention_days,
|
|
102
|
+
run_vacuum=config.run_vacuum,
|
|
103
|
+
enabled=config.enabled,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@router.get(
|
|
108
|
+
"/status",
|
|
109
|
+
response_model=MaintenanceStatusResponse,
|
|
110
|
+
summary="Get maintenance status",
|
|
111
|
+
description="Get maintenance system status and configuration",
|
|
112
|
+
)
|
|
113
|
+
async def get_maintenance_status() -> MaintenanceStatusResponse:
|
|
114
|
+
"""Get maintenance system status.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Maintenance system status and available tasks.
|
|
118
|
+
"""
|
|
119
|
+
manager = get_maintenance_manager()
|
|
120
|
+
config = manager.config
|
|
121
|
+
|
|
122
|
+
# Get available task names
|
|
123
|
+
available_tasks = [strategy.name for strategy in manager._strategies]
|
|
124
|
+
|
|
125
|
+
# Get next scheduled run from scheduler
|
|
126
|
+
scheduler = get_scheduler()
|
|
127
|
+
next_scheduled = scheduler.get_maintenance_next_run()
|
|
128
|
+
|
|
129
|
+
return MaintenanceStatusResponse(
|
|
130
|
+
enabled=config.enabled,
|
|
131
|
+
last_run_at=_last_maintenance_run,
|
|
132
|
+
next_scheduled_at=next_scheduled,
|
|
133
|
+
config=RetentionPolicyConfig(
|
|
134
|
+
validation_retention_days=config.validation_retention_days,
|
|
135
|
+
profile_keep_per_source=config.profile_keep_per_source,
|
|
136
|
+
notification_log_retention_days=config.notification_log_retention_days,
|
|
137
|
+
run_vacuum=config.run_vacuum,
|
|
138
|
+
enabled=config.enabled,
|
|
139
|
+
),
|
|
140
|
+
available_tasks=available_tasks,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@router.post(
|
|
145
|
+
"/cleanup",
|
|
146
|
+
response_model=MaintenanceReportResponse,
|
|
147
|
+
summary="Trigger cleanup",
|
|
148
|
+
description="Manually trigger database cleanup",
|
|
149
|
+
)
|
|
150
|
+
async def trigger_cleanup(
|
|
151
|
+
request: CleanupTriggerRequest,
|
|
152
|
+
) -> MaintenanceReportResponse:
|
|
153
|
+
"""Manually trigger database cleanup.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
request: Cleanup options.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Cleanup results report.
|
|
160
|
+
"""
|
|
161
|
+
global _last_maintenance_run
|
|
162
|
+
|
|
163
|
+
manager = get_maintenance_manager()
|
|
164
|
+
|
|
165
|
+
if request.tasks:
|
|
166
|
+
# Run specific tasks
|
|
167
|
+
started_at = datetime.utcnow()
|
|
168
|
+
results = []
|
|
169
|
+
|
|
170
|
+
for task_name in request.tasks:
|
|
171
|
+
result = await manager.run_task(task_name)
|
|
172
|
+
if result:
|
|
173
|
+
results.append(
|
|
174
|
+
CleanupResultItem(
|
|
175
|
+
task_name=result.task_name,
|
|
176
|
+
records_deleted=result.records_deleted,
|
|
177
|
+
duration_ms=result.duration_ms,
|
|
178
|
+
success=result.success,
|
|
179
|
+
error=result.error,
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
else:
|
|
183
|
+
results.append(
|
|
184
|
+
CleanupResultItem(
|
|
185
|
+
task_name=task_name,
|
|
186
|
+
records_deleted=0,
|
|
187
|
+
duration_ms=0,
|
|
188
|
+
success=False,
|
|
189
|
+
error=f"Task not found: {task_name}",
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Run vacuum if requested
|
|
194
|
+
vacuum_performed = False
|
|
195
|
+
vacuum_error = None
|
|
196
|
+
if request.run_vacuum:
|
|
197
|
+
try:
|
|
198
|
+
await manager.vacuum()
|
|
199
|
+
vacuum_performed = True
|
|
200
|
+
except Exception as e:
|
|
201
|
+
vacuum_error = str(e)
|
|
202
|
+
logger.error(f"VACUUM failed: {e}")
|
|
203
|
+
|
|
204
|
+
completed_at = datetime.utcnow()
|
|
205
|
+
_last_maintenance_run = completed_at
|
|
206
|
+
|
|
207
|
+
return MaintenanceReportResponse(
|
|
208
|
+
started_at=started_at,
|
|
209
|
+
completed_at=completed_at,
|
|
210
|
+
results=results,
|
|
211
|
+
total_deleted=sum(r.records_deleted for r in results),
|
|
212
|
+
total_duration_ms=int(
|
|
213
|
+
(completed_at - started_at).total_seconds() * 1000
|
|
214
|
+
),
|
|
215
|
+
vacuum_performed=vacuum_performed,
|
|
216
|
+
vacuum_error=vacuum_error,
|
|
217
|
+
success=all(r.success for r in results) and vacuum_error is None,
|
|
218
|
+
)
|
|
219
|
+
else:
|
|
220
|
+
# Run all cleanup tasks
|
|
221
|
+
# Temporarily override vacuum setting
|
|
222
|
+
original_vacuum = manager.config.run_vacuum
|
|
223
|
+
manager._config.run_vacuum = request.run_vacuum
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
report = await manager.run_cleanup()
|
|
227
|
+
finally:
|
|
228
|
+
manager._config.run_vacuum = original_vacuum
|
|
229
|
+
|
|
230
|
+
_last_maintenance_run = report.completed_at
|
|
231
|
+
|
|
232
|
+
return MaintenanceReportResponse(
|
|
233
|
+
started_at=report.started_at,
|
|
234
|
+
completed_at=report.completed_at,
|
|
235
|
+
results=[
|
|
236
|
+
CleanupResultItem(
|
|
237
|
+
task_name=r.task_name,
|
|
238
|
+
records_deleted=r.records_deleted,
|
|
239
|
+
duration_ms=r.duration_ms,
|
|
240
|
+
success=r.success,
|
|
241
|
+
error=r.error,
|
|
242
|
+
)
|
|
243
|
+
for r in report.results
|
|
244
|
+
],
|
|
245
|
+
total_deleted=report.total_deleted,
|
|
246
|
+
total_duration_ms=report.total_duration_ms,
|
|
247
|
+
vacuum_performed=report.vacuum_performed,
|
|
248
|
+
vacuum_error=report.vacuum_error,
|
|
249
|
+
success=report.success,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@router.post(
|
|
254
|
+
"/vacuum",
|
|
255
|
+
response_model=MaintenanceReportResponse,
|
|
256
|
+
summary="Run database vacuum",
|
|
257
|
+
description="Run SQLite VACUUM to reclaim disk space",
|
|
258
|
+
)
|
|
259
|
+
async def run_vacuum() -> MaintenanceReportResponse:
|
|
260
|
+
"""Run SQLite VACUUM to reclaim disk space.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Vacuum operation result.
|
|
264
|
+
"""
|
|
265
|
+
manager = get_maintenance_manager()
|
|
266
|
+
|
|
267
|
+
started_at = datetime.utcnow()
|
|
268
|
+
vacuum_error = None
|
|
269
|
+
|
|
270
|
+
try:
|
|
271
|
+
await manager.vacuum()
|
|
272
|
+
vacuum_performed = True
|
|
273
|
+
except Exception as e:
|
|
274
|
+
vacuum_error = str(e)
|
|
275
|
+
vacuum_performed = False
|
|
276
|
+
logger.error(f"VACUUM failed: {e}")
|
|
277
|
+
|
|
278
|
+
completed_at = datetime.utcnow()
|
|
279
|
+
|
|
280
|
+
return MaintenanceReportResponse(
|
|
281
|
+
started_at=started_at,
|
|
282
|
+
completed_at=completed_at,
|
|
283
|
+
results=[],
|
|
284
|
+
total_deleted=0,
|
|
285
|
+
total_duration_ms=int((completed_at - started_at).total_seconds() * 1000),
|
|
286
|
+
vacuum_performed=vacuum_performed,
|
|
287
|
+
vacuum_error=vacuum_error,
|
|
288
|
+
success=vacuum_error is None,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@router.get(
|
|
293
|
+
"/cache/stats",
|
|
294
|
+
response_model=CacheStatsResponse,
|
|
295
|
+
summary="Get cache statistics",
|
|
296
|
+
description="Get current cache usage statistics",
|
|
297
|
+
)
|
|
298
|
+
async def get_cache_stats() -> CacheStatsResponse:
|
|
299
|
+
"""Get cache statistics.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Cache usage statistics.
|
|
303
|
+
"""
|
|
304
|
+
cache = get_cache()
|
|
305
|
+
stats = await cache.get_stats()
|
|
306
|
+
|
|
307
|
+
return CacheStatsResponse(
|
|
308
|
+
total_entries=stats.get("total_entries", 0),
|
|
309
|
+
expired_entries=stats.get("expired_entries", 0),
|
|
310
|
+
valid_entries=stats.get("valid_entries", 0),
|
|
311
|
+
max_size=stats.get("max_size", 0),
|
|
312
|
+
hit_rate=None, # Not tracked by default
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
@router.post(
|
|
317
|
+
"/cache/clear",
|
|
318
|
+
response_model=CacheStatsResponse,
|
|
319
|
+
summary="Clear cache",
|
|
320
|
+
description="Clear cached data",
|
|
321
|
+
)
|
|
322
|
+
async def clear_cache(request: CacheClearRequest) -> CacheStatsResponse:
|
|
323
|
+
"""Clear cache data.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
request: Cache clear options.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Updated cache statistics.
|
|
330
|
+
"""
|
|
331
|
+
if request.namespace:
|
|
332
|
+
# Clear specific namespace
|
|
333
|
+
manager = get_cache_manager()
|
|
334
|
+
cache = manager.get(request.namespace)
|
|
335
|
+
if cache:
|
|
336
|
+
if request.pattern:
|
|
337
|
+
await cache.invalidate_pattern(request.pattern)
|
|
338
|
+
else:
|
|
339
|
+
await cache.clear()
|
|
340
|
+
else:
|
|
341
|
+
raise HTTPException(
|
|
342
|
+
status_code=404,
|
|
343
|
+
detail=f"Cache namespace not found: {request.namespace}",
|
|
344
|
+
)
|
|
345
|
+
else:
|
|
346
|
+
# Clear default cache
|
|
347
|
+
cache = get_cache()
|
|
348
|
+
if request.pattern:
|
|
349
|
+
await cache.invalidate_pattern(request.pattern)
|
|
350
|
+
else:
|
|
351
|
+
await cache.clear()
|
|
352
|
+
|
|
353
|
+
# Return updated stats
|
|
354
|
+
cache = get_cache()
|
|
355
|
+
stats = await cache.get_stats()
|
|
356
|
+
|
|
357
|
+
return CacheStatsResponse(
|
|
358
|
+
total_entries=stats.get("total_entries", 0),
|
|
359
|
+
expired_entries=stats.get("expired_entries", 0),
|
|
360
|
+
valid_entries=stats.get("valid_entries", 0),
|
|
361
|
+
max_size=stats.get("max_size", 0),
|
|
362
|
+
hit_rate=None,
|
|
363
|
+
)
|