Skip to content

Commit bd89c29

Browse files
authored
Merge pull request #2 from feedloop/feature/remove-system-config
Refactor: Remove system_config table and usage
2 parents 4a0baa7 + 078728d commit bd89c29

5 files changed

Lines changed: 46 additions & 44 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Qhronos (v0.1.0) is a developer-first scheduling and notification platform. It l
2727
**Event → Schedule → Occurrence Flow:**
2828
- **Event:** User-defined intent and configuration, stored in the database.
2929
- **Schedule:** Actionable plan (stored in Redis) for when the event should be executed. Created by the expander.
30-
- **Occurrence:** Created only after a scheduled event is executed (dispatched by the dispatcher). Represents the actual attempt to process (dispatch) the event at a specific time, with status and result information.
30+
- **Occurrence:** A record representing a specific scheduled instance of an Event. For recurring events, these are generated by the Expander. Each Occurrence tracks its lifecycle (e.g., pending, scheduled, dispatched, failed) and the outcome of its execution attempt by the Dispatcher.
3131

3232
For system architecture and in-depth design, see [design.md](./design.md).
3333

design.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ Qhronos is a developer-first, event scheduling and notification platform. It ena
4444
4545
4646
[Scheduler Layer] ──► [Redis]
47-
47+
(Core Scheduler, Expander, Dispatcher, Archival Scheduler)
4848
49-
[Background Jobs: Expander, Dispatcher, Cleanup]
49+
[Background Jobs: Expander, Dispatcher, Archival/Cleanup]
5050
5151
5252
[Webhook Delivery]
@@ -101,6 +101,8 @@ Qhronos uses JWT tokens and a master token for API authentication. Access contro
101101
### Middleware
102102
- Cross-cutting concerns: logging, error handling, authentication, rate limiting
103103
- Ensures consistent request/response processing and security
104+
- **Ginzap Logger:** Uses `zap` for structured logging of HTTP requests and responses, including latency, status, etc., via `ginzap.Ginzap` and `ginzap.RecoveryWithZap`.
105+
- **RequestIDMiddleware:** Injects a unique request ID into the context and logs for improved traceability.
104106

105107
### Services
106108
- Encapsulate business logic not directly tied to HTTP or data access
@@ -112,11 +114,12 @@ Qhronos uses JWT tokens and a master token for API authentication. Access contro
112114
- Maps Go models to database tables
113115

114116
### Scheduler Layer
115-
- Background jobs for event expansion, webhook dispatch, and cleanup
116-
- Expander: generates occurrences for recurring events
117-
- Dispatcher: delivers webhooks for due occurrences, handles retries
118-
- Cleanup: archives old data and enforces retention policies
119-
- Uses Redis for coordination and scheduling
117+
- Background jobs for event expansion, webhook dispatch, and data archival.
118+
- Uses Redis for coordination and scheduling of tasks.
119+
- **Core Scheduler (`scheduler.Scheduler`):** Manages the underlying scheduling mechanism in Redis, providing a foundation for other scheduler components like the Expander and Dispatcher to enqueue and manage scheduled tasks.
120+
- **Expander (`scheduler.Expander`):** Periodically scans for recurring events. For each event, it computes the next set of occurrences based on the `look_ahead_duration` and `expansion_interval` settings and stores them (e.g., in Redis via the Core Scheduler, for the Dispatcher to pick up).
121+
- **Dispatcher (`scheduler.Dispatcher`):** Retrieves due occurrences from the schedule (e.g., Redis, managed by Core Scheduler). It attempts to deliver the webhook to the event's configured URL, signing with HMAC if configured. Handles retries with backoff on failure, up to a maximum attempt count.
122+
- **Archival Scheduler (`scheduler.ArchivalScheduler`):** Periodically archives old events, occurrences, and webhook attempts from the primary tables to archive tables based on configured retention policies and archival interval. This helps manage database size and performance.
120123

121124
### Models
122125
- Defines data structures for events, occurrences, tokens, and errors
@@ -163,7 +166,7 @@ Qhronos uses JWT tokens and a master token for API authentication. Access contro
163166
- Exceeding the limit results in a 429 response.
164167

165168
### Archiving & Retention
166-
- The Cleanup job periodically archives old events, occurrences, and webhook attempts based on retention policies.
169+
- The **Archival Scheduler** job periodically archives old events, occurrences, and webhook attempts based on retention policies.
167170
- Archived data is moved to separate tables for long-term storage.
168171

169172
### Health & Status Endpoints
@@ -184,7 +187,6 @@ While the schema includes tables for analytics and performance metrics, the curr
184187
| +----< archived_occurrences >
185188
+----< archived_events >
186189
187-
[system_config] (global config)
188190
[analytics_daily], [analytics_hourly], [performance_metrics] (aggregates)
189191
```
190192

@@ -194,7 +196,6 @@ While the schema includes tables for analytics and performance metrics, the curr
194196
- **occurrences:** Each row represents a scheduled execution of an event, with status and delivery tracking.
195197
- **webhook_attempts:** Logs each attempt to deliver a webhook for an occurrence, including status and response.
196198
- **archived_events / archived_occurrences / archived_webhook_attempts:** Long-term storage for data past retention windows.
197-
- **system_config:** Stores global configuration and retention policies.
198199
- **analytics_daily / analytics_hourly / performance_metrics:** Tables for aggregated statistics and system performance. **Note:** These tables are present in the schema for future use. As of the current implementation, they are not yet populated or queried by the application logic. Enhanced analytics and reporting are planned for a future release.
199200

200201
## 7. API Reference
@@ -213,6 +214,7 @@ While the schema includes tables for analytics and performance metrics, the curr
213214
| POST | /tokens | Create a new JWT token (admin) |
214215
| GET | /status | Service status and health info |
215216
| GET | /health | Simple health check |
217+
| WS | /ws | Real-time event delivery via WebSocket |
216218

217219
### Request/Response Patterns
218220
- All endpoints use JSON for request and response bodies.

internal/scheduler/archiver.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package scheduler
22

33
import (
44
"context"
5-
"encoding/json"
65
"time"
76

87
"github.com/feedloop/qhronos/internal/config"
@@ -56,7 +55,7 @@ func updateLastArchivalTimeRedis(ctx context.Context, rdb *redis.Client, checkPe
5655
}
5756

5857
func syncRetentionConfigToDB(db *sqlx.DB, durations config.RetentionDurations) error {
59-
value := map[string]interface{}{
58+
/*value := map[string]interface{}{
6059
"events": map[string]interface{}{
6160
"max_past_occurrences": durations.Events.String(),
6261
},
@@ -73,7 +72,8 @@ func syncRetentionConfigToDB(db *sqlx.DB, durations config.RetentionDurations) e
7372
VALUES ($1, $2, $3, $4)
7473
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now(), updated_by = EXCLUDED.updated_by
7574
`, "retention_policies", valueJSON, "Data retention policies", "system")
76-
return err
75+
return err*/
76+
return nil // Prevent writing to system_config
7777
}
7878

7979
func StartArchivalScheduler(db *sqlx.DB, rdb *redis.Client, checkPeriod time.Duration, durations config.RetentionDurations, stopCh <-chan struct{}, logger *zap.Logger) {

internal/testutils/db.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ func TestDB(t testing.TB) *sqlx.DB {
3838

3939
// Check if migrations have already been run
4040
var count int
41-
err = db.GetContext(context.Background(), &count, "SELECT COUNT(*) FROM pg_tables WHERE tablename = 'system_config'")
41+
err = db.GetContext(context.Background(), &count, "SELECT COUNT(*) FROM pg_tables WHERE tablename = 'events'")
4242
if err != nil || count == 0 {
4343
// Run migrations
44-
migration, err := os.ReadFile("migrations/001_initial_schema.sql")
44+
migration, err := os.ReadFile("../../migrations/001_initial_schema.sql")
4545
if err != nil {
4646
t.Fatalf("Could not read migration file: %s", err)
4747
}
@@ -53,4 +53,4 @@ func TestDB(t testing.TB) *sqlx.DB {
5353
}
5454

5555
return db
56-
}
56+
}

migrations/001_initial_schema.sql

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,35 @@ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
66
-- Configuration Tables
77

88
-- System configuration including retention policies
9-
CREATE TABLE system_config (
10-
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
11-
key TEXT UNIQUE NOT NULL,
12-
value JSONB NOT NULL,
13-
description TEXT,
14-
updated_at TIMESTAMPTZ DEFAULT now(),
15-
updated_by TEXT NOT NULL
16-
);
9+
-- CREATE TABLE system_config (
10+
-- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
11+
-- key TEXT UNIQUE NOT NULL,
12+
-- value JSONB NOT NULL,
13+
-- description TEXT,
14+
-- updated_at TIMESTAMPTZ DEFAULT now(),
15+
-- updated_by TEXT NOT NULL
16+
-- );
1717

1818
-- Insert default retention policies
19-
INSERT INTO system_config (key, value, description, updated_by) VALUES
20-
('retention_policies', '{
21-
"logs": {
22-
"webhook_attempts": "30d",
23-
"api_requests": "90d",
24-
"error_logs": "180d",
25-
"performance_metrics": "365d"
26-
},
27-
"events": {
28-
"max_future_scheduling": "365d",
29-
"max_past_occurrences": "30d",
30-
"archived_events": "5y"
31-
},
32-
"analytics": {
33-
"hourly_metrics": "30d",
34-
"daily_metrics": "365d",
35-
"monthly_metrics": "5y"
36-
}
37-
}', 'Data retention policies in days (d) or years (y)', 'system');
19+
-- INSERT INTO system_config (key, value, description, updated_by) VALUES
20+
-- ('retention_policies', '{
21+
-- "logs": {
22+
-- "webhook_attempts": "30d",
23+
-- "api_requests": "90d",
24+
-- "error_logs": "180d",
25+
-- "performance_metrics": "365d"
26+
-- },
27+
-- "events": {
28+
-- "max_future_scheduling": "365d",
29+
-- "max_past_occurrences": "30d",
30+
-- "archived_events": "5y"
31+
-- },
32+
-- "analytics": {
33+
-- "hourly_metrics": "30d",
34+
-- "daily_metrics": "365d",
35+
-- "monthly_metrics": "5y"
36+
-- }
37+
-- }', 'Data retention policies in days (d) or years (y)', 'system');
3838

3939
-- Core Tables with Retention Policies
4040

0 commit comments

Comments
 (0)