diff --git a/relay.pid b/relay.pid index 3605470..73e27fc 100644 --- a/relay.pid +++ b/relay.pid @@ -1 +1 @@ -1946657 +1950899 diff --git a/src/config.c b/src/config.c index 805d7b0..1d18122 100644 --- a/src/config.c +++ b/src/config.c @@ -951,6 +951,22 @@ static int validate_config_field(const char* key, const char* value, char* error } return 0; } + + // SQLite performance tuning + if (strcmp(key, "sqlite_mmap_size") == 0) { + if (!is_valid_positive_integer(value) && strcmp(value, "0") != 0) { + snprintf(error_msg, error_size, "invalid sqlite_mmap_size '%s' (must be non-negative integer bytes)", value); + return -1; + } + return 0; + } + if (strcmp(key, "sqlite_cache_size_kb") == 0) { + if (!is_valid_positive_integer(value)) { + snprintf(error_msg, error_size, "invalid sqlite_cache_size_kb '%s' (must be positive integer KB)", value); + return -1; + } + return 0; + } // Boolean fields if (strcmp(key, "auth_enabled") == 0 || @@ -1719,7 +1735,9 @@ int populate_default_config_values(void) { strcmp(key, "default_limit") == 0 || strcmp(key, "max_limit") == 0 || strcmp(key, "nip42_challenge_expiration") == 0 || - strcmp(key, "nip40_expiration_grace_period") == 0) { + strcmp(key, "nip40_expiration_grace_period") == 0 || + strcmp(key, "sqlite_mmap_size") == 0 || + strcmp(key, "sqlite_cache_size_kb") == 0) { data_type = "integer"; } else if (strcmp(key, "auth_enabled") == 0 || strcmp(key, "nip40_expiration_enabled") == 0 || @@ -4499,7 +4517,9 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela strcmp(key, "default_limit") == 0 || strcmp(key, "max_limit") == 0 || strcmp(key, "nip42_challenge_expiration") == 0 || - strcmp(key, "nip40_expiration_grace_period") == 0) { + strcmp(key, "nip40_expiration_grace_period") == 0 || + strcmp(key, "sqlite_mmap_size") == 0 || + strcmp(key, "sqlite_cache_size_kb") == 0) { data_type = "integer"; } else if (strcmp(key, "auth_enabled") == 0 || strcmp(key, "nip40_expiration_enabled") == 0 || diff --git a/src/default_config_event.h b/src/default_config_event.h index 4d36fb6..483ce1f 100644 --- a/src/default_config_event.h +++ b/src/default_config_event.h @@ -83,6 +83,14 @@ static const struct { // IP-based rate limiting or access control (which would require firewall protection anyway) {"trust_proxy_headers", "true"}, + // SQLite Performance Tuning + // mmap_size: bytes of database file to memory-map (0 = disabled, 268435456 = 256MB recommended) + // Eliminates pread64 syscall overhead for database reads — significant CPU savings under load + {"sqlite_mmap_size", "268435456"}, + // cache_size_kb: SQLite page cache size in KB (negative = KB, positive = pages of 4KB each) + // Default 2000KB is too small for a busy relay; 65536KB (64MB) keeps hot data in memory + {"sqlite_cache_size_kb", "65536"}, + // NIP-59 Gift Wrap Timestamp Configuration {"nip59_timestamp_max_delay_sec", "0"}, diff --git a/src/main.c b/src/main.c index d049588..b952599 100644 --- a/src/main.c +++ b/src/main.c @@ -566,6 +566,40 @@ int init_database(const char* database_path_override) { DEBUG_LOG("SQLite WAL mode enabled"); } + // Apply SQLite performance tuning PRAGMAs from config + // mmap_size: memory-map the database file to eliminate pread64 syscall overhead + // Default 256MB covers most relay databases; set to 0 to disable + long mmap_size = get_config_int("sqlite_mmap_size", 268435456); + if (mmap_size > 0) { + char mmap_pragma[64]; + snprintf(mmap_pragma, sizeof(mmap_pragma), "PRAGMA mmap_size=%ld;", mmap_size); + char* mmap_error = NULL; + rc = sqlite3_exec(g_db, mmap_pragma, NULL, NULL, &mmap_error); + if (rc != SQLITE_OK) { + DEBUG_WARN("Failed to set mmap_size: %s", mmap_error ? mmap_error : "unknown"); + if (mmap_error) sqlite3_free(mmap_error); + } else { + DEBUG_LOG("SQLite mmap_size set to %ld bytes", mmap_size); + } + } + + // cache_size_kb: page cache size in KB (negative value = KB, positive = number of 4KB pages) + // Default 64MB keeps hot event data in memory and reduces repeated disk reads + int cache_size_kb = get_config_int("sqlite_cache_size_kb", 65536); + if (cache_size_kb != 0) { + char cache_pragma[64]; + // Use negative value so SQLite interprets it as KB rather than page count + snprintf(cache_pragma, sizeof(cache_pragma), "PRAGMA cache_size=-%d;", cache_size_kb > 0 ? cache_size_kb : -cache_size_kb); + char* cache_error = NULL; + rc = sqlite3_exec(g_db, cache_pragma, NULL, NULL, &cache_error); + if (rc != SQLITE_OK) { + DEBUG_WARN("Failed to set cache_size: %s", cache_error ? cache_error : "unknown"); + if (cache_error) sqlite3_free(cache_error); + } else { + DEBUG_LOG("SQLite cache_size set to %d KB", cache_size_kb); + } + } + DEBUG_TRACE("Exiting init_database() - success"); return 0; } diff --git a/src/main.h b/src/main.h index 0c61183..475b8e0 100644 --- a/src/main.h +++ b/src/main.h @@ -13,8 +13,8 @@ // Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros #define CRELAY_VERSION_MAJOR 1 #define CRELAY_VERSION_MINOR 2 -#define CRELAY_VERSION_PATCH 6 -#define CRELAY_VERSION "v1.2.6" +#define CRELAY_VERSION_PATCH 7 +#define CRELAY_VERSION "v1.2.7" // Relay metadata (authoritative source for NIP-11 information) #define RELAY_NAME "C-Relay"