Skip to content

Commit 9e1c652

Browse files
committed
Update version to 8.1.3 and enhance database configuration for Synology NAS support
- Bumped version number in version.txt to 8.1.3. - Added detection for Synology NAS in database.py to apply performance optimizations. - Implemented specific database settings for Synology, including adjusted synchronous mode, cache size, and memory mapping. - Enhanced logging to monitor optimization status for Synology configurations.
1 parent 5dc7204 commit 9e1c652

File tree

2 files changed

+119
-13
lines changed

2 files changed

+119
-13
lines changed

src/primary/utils/database.py

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8.1.2
1+
8.1.3

0 commit comments

Comments
 (0)