@@ -45,11 +45,33 @@ def _configure_connection(self, conn):
4545 logger .warning (f"WAL mode failed, using DELETE mode: { wal_error } " )
4646 conn .execute ('PRAGMA journal_mode = DELETE' )
4747
48- # Enhanced settings for Docker rebuild resilience and corruption prevention
49- conn .execute ('PRAGMA synchronous = FULL' ) # Maximum durability for Docker environments
50- conn .execute ('PRAGMA cache_size = -32000' ) # 32MB cache for better performance
51- conn .execute ('PRAGMA temp_store = MEMORY' ) # Store temp tables in memory
52- conn .execute ('PRAGMA busy_timeout = 60000' ) # 60 seconds for Docker I/O delays
48+ # Detect if running on Synology NAS for performance optimizations
49+ is_synology = self ._detect_synology_nas ()
50+ synology_opt_enabled = os .environ .get ('HUNTARR_SYNOLOGY_OPTIMIZATIONS' , 'true' ).lower () == 'true'
51+
52+ if is_synology and synology_opt_enabled :
53+ logger .info ("Synology NAS detected - applying performance optimizations for network file systems" )
54+ # Synology-optimized settings for network file system performance
55+ conn .execute ('PRAGMA synchronous = NORMAL' ) # Much faster on NFS/CIFS, still safe with WAL
56+ conn .execute ('PRAGMA cache_size = -40960' ) # 40MB cache for better performance on Synology
57+ conn .execute ('PRAGMA temp_store = MEMORY' ) # Store temp tables in memory
58+ conn .execute ('PRAGMA busy_timeout = 30000' ) # 30 seconds for Synology I/O
59+ conn .execute ('PRAGMA mmap_size = 134217728' ) # 128MB memory map optimized for Synology
60+ else :
61+ if is_synology and not synology_opt_enabled :
62+ logger .info ("Synology NAS detected but optimizations disabled via HUNTARR_SYNOLOGY_OPTIMIZATIONS=false" )
63+ # Standard settings for Docker rebuild resilience and corruption prevention
64+ conn .execute ('PRAGMA synchronous = FULL' ) # Maximum durability for Docker environments
65+ conn .execute ('PRAGMA cache_size = -32000' ) # 32MB cache for better performance
66+ conn .execute ('PRAGMA temp_store = MEMORY' ) # Store temp tables in memory
67+ conn .execute ('PRAGMA busy_timeout = 60000' ) # 60 seconds for Docker I/O delays
68+
69+ # Skip mmap on Windows if it causes issues, but use it elsewhere for performance
70+ import platform
71+ if platform .system () != "Windows" :
72+ conn .execute ('PRAGMA mmap_size = 268435456' ) # 256MB memory map
73+
74+ # Common settings for all platforms
5375 conn .execute ('PRAGMA auto_vacuum = INCREMENTAL' ) # Incremental vacuum for maintenance
5476 conn .execute ('PRAGMA secure_delete = ON' ) # Secure deletion to prevent data recovery
5577
@@ -62,21 +84,29 @@ def _configure_connection(self, conn):
6284 # Enhanced checkpoint settings for WAL mode
6385 result = conn .execute ('PRAGMA journal_mode' ).fetchone ()
6486 if result and result [0 ] == 'wal' :
65- conn .execute ('PRAGMA wal_autocheckpoint = 500' ) # More frequent checkpoints for Docker
66- conn .execute ('PRAGMA journal_size_limit = 67108864' ) # 64MB journal size limit
87+ if is_synology and synology_opt_enabled :
88+ # More aggressive checkpointing for Synology
89+ conn .execute ('PRAGMA wal_autocheckpoint = 1000' ) # Less frequent checkpoints for performance
90+ conn .execute ('PRAGMA journal_size_limit = 134217728' ) # 128MB journal size limit
91+ else :
92+ # Conservative checkpointing for other systems
93+ conn .execute ('PRAGMA wal_autocheckpoint = 500' ) # More frequent checkpoints for Docker
94+ conn .execute ('PRAGMA journal_size_limit = 67108864' ) # 64MB journal size limit
6795 conn .execute ('PRAGMA wal_checkpoint(TRUNCATE)' ) # Force checkpoint and truncate
6896
69- # Skip mmap on Windows if it causes issues, but use it elsewhere for performance
70- import platform
71- if platform .system () != "Windows" :
72- conn .execute ('PRAGMA mmap_size = 268435456' ) # 256MB memory map
73-
7497 # Test the configuration worked
7598 integrity_result = conn .execute ('PRAGMA integrity_check' ).fetchone ()
7699 if integrity_result and integrity_result [0 ] != 'ok' :
77100 logger .error (f"Database integrity check failed: { integrity_result } " )
78101 raise sqlite3 .DatabaseError (f"Database integrity compromised: { integrity_result } " )
79102
103+ # Log optimization status for monitoring
104+ if is_synology :
105+ sync_mode = conn .execute ('PRAGMA synchronous' ).fetchone ()[0 ]
106+ cache_size = conn .execute ('PRAGMA cache_size' ).fetchone ()[0 ]
107+ mmap_size = conn .execute ('PRAGMA mmap_size' ).fetchone ()[0 ]
108+ logger .info (f"Synology optimizations applied: sync={ sync_mode } , cache={ abs (cache_size / 1024 ):.1f} MB, mmap={ mmap_size / 1024 / 1024 :.0f} MB" )
109+
80110 except Exception as e :
81111 logger .error (f"Error configuring database connection: { e } " )
82112 # Continue with basic connection if configuration fails
@@ -132,6 +162,82 @@ def _get_database_path(self) -> Path:
132162 data_dir .mkdir (parents = True , exist_ok = True )
133163 return data_dir / "huntarr.db"
134164
165+ def _detect_synology_nas (self ):
166+ """
167+ Detect if running on Synology NAS using multiple reliable methods.
168+ Returns True if Synology is detected, False otherwise.
169+ """
170+ try :
171+ # Method 1: Check for Synology-specific files and directories
172+ synology_indicators = [
173+ '/usr/syno' , # Synology system directory
174+ '/etc/synoinfo.conf' , # Synology configuration file
175+ '/proc/sys/kernel/syno_hw_version' , # Synology hardware version
176+ '/usr/bin/synopkg' , # Synology package manager
177+ '/var/services' # Synology services directory
178+ ]
179+
180+ for indicator in synology_indicators :
181+ if os .path .exists (indicator ):
182+ logger .debug (f"Synology detected via: { indicator } " )
183+ return True
184+
185+ # Method 2: Check environment variables set by Synology
186+ synology_env_vars = [
187+ 'SYNOPKG_PKGNAME' ,
188+ 'SYNOPKG_PKGVER' ,
189+ 'SYNO_USER' ,
190+ 'SYNO_GROUP'
191+ ]
192+
193+ for env_var in synology_env_vars :
194+ if os .environ .get (env_var ):
195+ logger .debug (f"Synology detected via environment variable: { env_var } " )
196+ return True
197+
198+ # Method 3: Check system information files for Synology
199+ info_files = [
200+ ('/etc/os-release' , 'synology' ),
201+ ('/proc/version' , 'synology' ),
202+ ('/etc/issue' , 'synology' )
203+ ]
204+
205+ for file_path , keyword in info_files :
206+ try :
207+ if os .path .exists (file_path ):
208+ with open (file_path , 'r' ) as f :
209+ content = f .read ().lower ()
210+ if keyword in content :
211+ logger .debug (f"Synology detected in { file_path } " )
212+ return True
213+ except (IOError , OSError ):
214+ continue
215+
216+ # Method 4: Check for Synology-specific hostname patterns
217+ try :
218+ hostname = os .uname ().nodename .lower ()
219+ if 'diskstation' in hostname or 'rackstation' in hostname :
220+ logger .debug (f"Synology detected via hostname: { hostname } " )
221+ return True
222+ except (AttributeError , OSError ):
223+ pass
224+
225+ # Method 5: Check for Synology Docker environment
226+ try :
227+ with open ('/proc/1/cgroup' , 'r' ) as f :
228+ cgroup_content = f .read ()
229+ if 'synology' in cgroup_content .lower ():
230+ logger .debug ("Synology detected via Docker cgroup" )
231+ return True
232+ except (IOError , OSError ):
233+ pass
234+
235+ return False
236+
237+ except Exception as e :
238+ logger .debug (f"Error during Synology detection: { e } " )
239+ return False
240+
135241 def _handle_database_corruption (self ):
136242 """Handle database corruption by creating backup and starting fresh"""
137243 import time
0 commit comments