v0.7.34 - We seemed to maybe finally fixed the monitoring error?

This commit is contained in:
Your Name
2025-10-22 10:19:43 -04:00
parent 9cb9b746d8
commit 9179d57cc9
21 changed files with 2877 additions and 503 deletions

View File

@@ -238,25 +238,76 @@ void free_subscription(subscription_t* sub) {
// Add subscription to global manager (thread-safe)
int add_subscription_to_manager(subscription_t* sub) {
if (!sub) return -1;
pthread_mutex_lock(&g_subscription_manager.subscriptions_lock);
// Check global limits
if (g_subscription_manager.total_subscriptions >= g_subscription_manager.max_total_subscriptions) {
// Check for existing subscription with same ID and WebSocket connection
// Remove it first to prevent duplicates (implements subscription replacement per NIP-01)
subscription_t** current = &g_subscription_manager.active_subscriptions;
int found_duplicate = 0;
subscription_t* duplicate_old = NULL;
while (*current) {
subscription_t* existing = *current;
// Match by subscription ID and WebSocket pointer
if (strcmp(existing->id, sub->id) == 0 && existing->wsi == sub->wsi) {
// Found duplicate: mark inactive and unlink from global list under lock
existing->active = 0;
*current = existing->next;
g_subscription_manager.total_subscriptions--;
found_duplicate = 1;
duplicate_old = existing; // defer free until after per-session unlink
break;
}
current = &(existing->next);
}
// Check global limits (only if not replacing an existing subscription)
if (!found_duplicate && g_subscription_manager.total_subscriptions >= g_subscription_manager.max_total_subscriptions) {
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
DEBUG_ERROR("Maximum total subscriptions reached");
return -1;
}
// Add to global list
sub->next = g_subscription_manager.active_subscriptions;
g_subscription_manager.active_subscriptions = sub;
g_subscription_manager.total_subscriptions++;
g_subscription_manager.total_created++;
// Only increment total_created if this is a new subscription (not a replacement)
if (!found_duplicate) {
g_subscription_manager.total_created++;
}
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
// Log subscription creation to database
// If we replaced an existing subscription, unlink it from the per-session list before freeing
if (duplicate_old) {
// Obtain per-session data for this wsi
struct per_session_data* pss = (struct per_session_data*) lws_wsi_user(duplicate_old->wsi);
if (pss) {
pthread_mutex_lock(&pss->session_lock);
struct subscription** scur = &pss->subscriptions;
while (*scur) {
if (*scur == duplicate_old) {
// Unlink by pointer identity to avoid removing the newly-added one
*scur = duplicate_old->session_next;
if (pss->subscription_count > 0) {
pss->subscription_count--;
}
break;
}
scur = &((*scur)->session_next);
}
pthread_mutex_unlock(&pss->session_lock);
}
// Now safe to free the old subscription
free_subscription(duplicate_old);
}
// Log subscription creation to database (INSERT OR REPLACE handles duplicates)
log_subscription_created(sub);
return 0;
@@ -324,10 +375,7 @@ int remove_subscription_from_manager(const char* sub_id, struct lws* wsi) {
// Check if an event matches a subscription filter
int event_matches_filter(cJSON* event, subscription_filter_t* filter) {
DEBUG_TRACE("Checking event against subscription filter");
if (!event || !filter) {
DEBUG_TRACE("Exiting event_matches_filter - null parameters");
return 0;
}
@@ -503,7 +551,6 @@ int event_matches_filter(cJSON* event, subscription_filter_t* filter) {
}
}
DEBUG_TRACE("Exiting event_matches_filter - match found");
return 1; // All filters passed
}
@@ -526,10 +573,7 @@ int event_matches_subscription(cJSON* event, subscription_t* subscription) {
// Broadcast event to all matching subscriptions (thread-safe)
int broadcast_event_to_subscriptions(cJSON* event) {
DEBUG_TRACE("Broadcasting event to subscriptions");
if (!event) {
DEBUG_TRACE("Exiting broadcast_event_to_subscriptions - null event");
return 0;
}
@@ -611,12 +655,19 @@ int broadcast_event_to_subscriptions(cJSON* event) {
if (buf) {
memcpy(buf + LWS_PRE, msg_str, msg_len);
// Send to WebSocket connection with error checking
// Note: lws_write can fail if connection is closed, but won't crash
int write_result = lws_write(current_temp->wsi, buf + LWS_PRE, msg_len, LWS_WRITE_TEXT);
if (write_result >= 0) {
// DEBUG: Log WebSocket frame details before sending
DEBUG_TRACE("WS_FRAME_SEND: type=EVENT sub=%s len=%zu data=%.100s%s",
current_temp->id,
msg_len,
msg_str,
msg_len > 100 ? "..." : "");
// Queue message for proper libwebsockets pattern
struct per_session_data* pss = (struct per_session_data*)lws_wsi_user(current_temp->wsi);
if (queue_message(current_temp->wsi, pss, msg_str, msg_len, LWS_WRITE_TEXT) == 0) {
// Message queued successfully
broadcasts++;
// Update events sent counter for this subscription
pthread_mutex_lock(&g_subscription_manager.subscriptions_lock);
subscription_t* update_sub = g_subscription_manager.active_subscriptions;
@@ -630,12 +681,14 @@ int broadcast_event_to_subscriptions(cJSON* event) {
update_sub = update_sub->next;
}
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
// Log event broadcast to database (optional - can be disabled for performance)
cJSON* event_id_obj = cJSON_GetObjectItem(event, "id");
if (event_id_obj && cJSON_IsString(event_id_obj)) {
log_event_broadcast(cJSON_GetStringValue(event_id_obj), current_temp->id, current_temp->client_ip);
}
} else {
DEBUG_ERROR("Failed to queue EVENT message for sub=%s", current_temp->id);
}
free(buf);
@@ -660,7 +713,6 @@ int broadcast_event_to_subscriptions(cJSON* event) {
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
DEBUG_LOG("Event broadcast complete: %d subscriptions matched", broadcasts);
DEBUG_TRACE("Exiting broadcast_event_to_subscriptions");
return broadcasts;
}
@@ -707,6 +759,10 @@ int has_subscriptions_for_kind(int event_kind) {
void log_subscription_created(const subscription_t* sub) {
if (!g_db || !sub) return;
// Convert wsi pointer to string
char wsi_str[32];
snprintf(wsi_str, sizeof(wsi_str), "%p", (void*)sub->wsi);
// Create filter JSON for logging
char* filter_json = NULL;
if (sub->filters) {
@@ -753,16 +809,18 @@ void log_subscription_created(const subscription_t* sub) {
cJSON_Delete(filters_array);
}
// Use INSERT OR REPLACE to handle duplicates automatically
const char* sql =
"INSERT INTO subscription_events (subscription_id, client_ip, event_type, filter_json) "
"VALUES (?, ?, 'created', ?)";
"INSERT OR REPLACE INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type, filter_json) "
"VALUES (?, ?, ?, 'created', ?)";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
if (rc == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, sub->id, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, sub->client_ip, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, filter_json ? filter_json : "[]", -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, wsi_str, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 3, sub->client_ip, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 4, filter_json ? filter_json : "[]", -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
@@ -777,8 +835,8 @@ void log_subscription_closed(const char* sub_id, const char* client_ip, const ch
if (!g_db || !sub_id) return;
const char* sql =
"INSERT INTO subscription_events (subscription_id, client_ip, event_type) "
"VALUES (?, ?, 'closed')";
"INSERT INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type) "
"VALUES (?, '', ?, 'closed')";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
@@ -792,7 +850,7 @@ void log_subscription_closed(const char* sub_id, const char* client_ip, const ch
// Update the corresponding 'created' entry with end time and events sent
const char* update_sql =
"UPDATE subscription_events "
"UPDATE subscriptions "
"SET ended_at = strftime('%s', 'now') "
"WHERE subscription_id = ? AND event_type = 'created' AND ended_at IS NULL";
@@ -810,7 +868,7 @@ void log_subscription_disconnected(const char* client_ip) {
// Mark all active subscriptions for this client as disconnected
const char* sql =
"UPDATE subscription_events "
"UPDATE subscriptions "
"SET ended_at = strftime('%s', 'now') "
"WHERE client_ip = ? AND event_type = 'created' AND ended_at IS NULL";
@@ -825,8 +883,8 @@ void log_subscription_disconnected(const char* client_ip) {
if (changes > 0) {
// Log a disconnection event
const char* insert_sql =
"INSERT INTO subscription_events (subscription_id, client_ip, event_type) "
"VALUES ('disconnect', ?, 'disconnected')";
"INSERT INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type) "
"VALUES ('disconnect', '', ?, 'disconnected')";
rc = sqlite3_prepare_v2(g_db, insert_sql, -1, &stmt, NULL);
if (rc == SQLITE_OK) {
@@ -863,7 +921,7 @@ void update_subscription_events_sent(const char* sub_id, int events_sent) {
if (!g_db || !sub_id) return;
const char* sql =
"UPDATE subscription_events "
"UPDATE subscriptions "
"SET events_sent = ? "
"WHERE subscription_id = ? AND event_type = 'created'";