Skip to content

Commit d81420f

Browse files
Merge pull request #8880 from jdarwood007/3.0/feature7512
2 parents a04a0e8 + 16fab22 commit d81420f

File tree

4 files changed

+111
-14
lines changed

4 files changed

+111
-14
lines changed

Languages/en_US/Help.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@
258258
$helptxt['cache_enable'] = 'SMF performs caching at a variety of levels. The higher the level of caching enabled the more CPU time will be spent retrieving cached information. If caching is available on your machine it is recommended that you try caching at level 1 first.';
259259
$helptxt['cache_memcached'] = 'If you are using memcached you need to provide the server details. This should be entered as a comma separated list as shown in the example below:<br><br> &quot;server1,server2,server3:port,server4&quot;<br><br>Note that if no port is specified SMF will use port 11211 unless the host contains a slash, then it is assumed to be an alternative transport and the port will be set to 0. SMF will attempt to perform rough/random load balancing across the specified servers.';
260260
$helptxt['cache_cachedir'] = 'This setting is only for the smf file-based cache system. It specifies the path to the cache directory. It is recommended that you place this in /tmp/ if you are going to use this, although it will work in any directory';
261-
$helptxt['cache_sqlite_cachedir'] = 'This setting is only for the SQLite database cache system. It specifies the path to the cache directory. It is recommended that you place this in /tmp/ if you are going to use this, although it will work in any directory';
261+
$helptxt['cache_sqlite_cachedir'] = 'This setting is only for the SQLite database cache system. It specifies the path to the cache directory. It is recommended that you place this in /tmp/ if you are going to use this, although it will work in any directory<br>';
262262
$helptxt['enableErrorLogging'] = 'This will log any errors, like a failed login, so you can see what went wrong.';
263263
$helptxt['enableErrorQueryLogging'] = 'This will include the full query sent to the database in the error log. It requires error logging to be turned on.<br><br><strong>Note: This will affect the ability to filter the error log by the error message.</strong>';
264264
$helptxt['disallow_sendBody'] = 'This setting removes the option to receive the text of replies, posts, and personal messages in notification emails.<br><br>Often, members will reply to the notification email, which in most cases means the webmaster receives the reply.';

Languages/en_US/ManageSettings.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@
152152
$txt['cache_memcached_settings'] = 'Memcache/Memcached settings';
153153
$txt['cache_memcached_servers'] = 'Memcache/Memcached servers';
154154
$txt['cache_memcached_servers_subtext'] = 'Example: 127.0.0.1:11211,127.0.0.2';
155+
$txt['cache_sqlite_wal'] = 'Enable SQLite3 WAL';
156+
$txt['cache_sqlite_wal_subtext'] = 'Read <a href="https://www.sqlite.org/wal.html">Write-Ahead Logging</a> documentation prior to using. If you enable then disable this setting, the database will become readonly.';
155157

156158
$txt['loadavg_warning'] = 'Please note: the settings below are to be edited with care. Setting any of them too low may render your forum <strong>unusable</strong>!';
157159
$txt['loadavg_enable'] = 'Enable load balancing by load averages';

Sources/Cache/APIs/Sqlite.php

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use SMF\Cache\CacheApi;
1919
use SMF\Cache\CacheApiInterface;
2020
use SMF\Config;
21+
use SMF\ErrorHandler;
2122
use SMF\Lang;
2223
use SMF\Utils;
2324
use SQLite3;
@@ -33,6 +34,12 @@
3334
*/
3435
class Sqlite extends CacheApi implements CacheApiInterface
3536
{
37+
/*****************
38+
* Class constants
39+
*****************/
40+
41+
public const CLASS_KEY = 'cache_sqlite';
42+
3643
/*********************
3744
* Internal properties
3845
*********************/
@@ -47,6 +54,12 @@ class Sqlite extends CacheApi implements CacheApiInterface
4754
*/
4855
private $cacheDB = null;
4956

57+
/**
58+
* Indicates we have logged a error.
59+
* @var bool
60+
*/
61+
private bool $logOnce = false;
62+
5063
/****************
5164
* Public methods
5265
****************/
@@ -65,15 +78,40 @@ public function __construct()
6578
public function connect(): bool
6679
{
6780
$database = $this->cachedir . '/' . 'SQLite3Cache.db3';
68-
$this->cacheDB = new SQLite3($database);
69-
$this->cacheDB->busyTimeout(1000);
7081

71-
if (filesize($database) == 0) {
72-
$this->cacheDB->exec('CREATE TABLE cache (key text unique, value blob, ttl int);');
73-
$this->cacheDB->exec('CREATE INDEX ttls ON cache(ttl);');
82+
if (!is_writable($database) || !is_writeable($this->cachedir)) {
83+
return false;
7484
}
7585

76-
return true;
86+
try {
87+
// Did we disable WAL? That triggers a read only mode, dump the cache.
88+
if (file_exists($this->cachedir . '/' . 'SQLite3Cache.db3-wal') && empty(Config::$cache_sqlite_wal)) {
89+
unlink($this->cachedir . '/' . 'SQLite3Cache.db3');
90+
}
91+
92+
$this->cacheDB = new SQLite3($database);
93+
$this->cacheDB->busyTimeout(1000);
94+
$this->cacheDB->enableExceptions(true);
95+
96+
// Its a WALuigi!
97+
if (!empty(Config::$cache_sqlite_wal)) {
98+
$this->cacheDB->exec('PRAGMA journal_mode = wal;');
99+
}
100+
101+
if (filesize($database) == 0) {
102+
$this->cacheDB->exec('CREATE TABLE cache (key text unique, value blob, ttl int);');
103+
$this->cacheDB->exec('CREATE INDEX ttls ON cache(ttl);');
104+
}
105+
106+
return true;
107+
} catch (\Exception $ex) {
108+
if (!$this->logOnce) {
109+
$this->logOnce = true;
110+
ErrorHandler::log(self::class . ':' . $ex->getMessage());
111+
}
112+
113+
return false;
114+
}
77115
}
78116

79117
/**
@@ -96,7 +134,17 @@ public function isSupported(bool $test = false): bool
96134
public function getData(string $key, ?int $ttl = null): mixed
97135
{
98136
$query = 'SELECT value FROM cache WHERE key = \'' . $this->cacheDB->escapeString($key) . '\' AND ttl >= ' . time() . ' LIMIT 1';
99-
$result = $this->cacheDB->query($query);
137+
138+
try {
139+
$result = $this->cacheDB->query($query);
140+
} catch (\Exception $ex) {
141+
if (!$this->logOnce) {
142+
$this->logOnce = true;
143+
ErrorHandler::log(self::class . ':' . $ex->getMessage());
144+
}
145+
146+
return null;
147+
}
100148

101149
$value = null;
102150

@@ -119,7 +167,17 @@ public function putData(string $key, mixed $value, ?int $ttl = null): mixed
119167
} else {
120168
$query = 'REPLACE INTO cache VALUES (\'' . $this->cacheDB->escapeString($key) . '\', \'' . $this->cacheDB->escapeString(\is_bool($value) ? \strval(\intval($value)) : $value) . '\', ' . $ttl . ');';
121169
}
122-
$result = $this->cacheDB->exec($query);
170+
171+
try {
172+
$result = $this->cacheDB->exec($query);
173+
} catch (\Exception $ex) {
174+
if (!$this->logOnce) {
175+
$this->logOnce = true;
176+
ErrorHandler::log(self::class . ':' . $ex->getMessage());
177+
}
178+
179+
return false;
180+
}
123181

124182
return $result;
125183
}
@@ -135,10 +193,19 @@ public function cleanCache(string $type = ''): bool
135193
$query = 'DELETE FROM cache;';
136194
}
137195

138-
$result = $this->cacheDB->exec($query);
196+
try {
197+
$result = $this->cacheDB->exec($query);
198+
199+
$query = 'VACUUM;';
200+
$this->cacheDB->exec($query);
201+
} catch (\Exception $ex) {
202+
if (!$this->logOnce) {
203+
$this->logOnce = true;
204+
ErrorHandler::log(self::class . ':' . $ex->getMessage());
205+
}
139206

140-
$query = 'VACUUM;';
141-
$this->cacheDB->exec($query);
207+
$result = false;
208+
}
142209

143210
$this->invalidateCache();
144211

@@ -153,14 +220,22 @@ public function cacheSettings(array &$config_vars): void
153220
$class_name = $this->getImplementationClassKeyName();
154221
$class_name_txt_key = strtolower($class_name);
155222

156-
$config_vars[] = Lang::getTxt('cache_' . $class_name_txt_key . '_settings', file: 'ManageSettings');
223+
$config_vars[] = Lang::getTxt(self::CLASS_KEY . '_settings', file: 'ManageSettings');
157224
$config_vars[] = [
158225
'cachedir_' . $class_name_txt_key,
159226
Lang::getTxt('cachedir_' . $class_name_txt_key, file: 'ManageSettings'),
160227
'file',
161228
'text',
162229
36,
163-
'cache_' . $class_name_txt_key . '_cachedir',
230+
self::CLASS_KEY . '_cachedir',
231+
];
232+
$config_vars[] = [
233+
self::CLASS_KEY . '_wal',
234+
Lang::getTxt('cache_sqlite_wal', file: 'ManageSettings'),
235+
'file',
236+
'check',
237+
self::CLASS_KEY . '_cachedir',
238+
'subtext' => Lang::getTxt(self::CLASS_KEY . '_wal_subtext', file: 'ManageSettings'),
164239
];
165240

166241
if (!isset(Utils::$context['settings_post_javascript'])) {

Sources/Config.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ class Config
207207
*/
208208
public static string $cachedir_sqlite;
209209

210+
/**
211+
* @var bool
212+
*
213+
* This is only used for the SQLite3 cache system.
214+
* Whether to enable Write-Ahead Logging.
215+
*/
216+
public static bool $cache_sqlite_wal;
217+
210218
########## Image proxy ##########
211219
/**
212220
* @var bool
@@ -691,6 +699,18 @@ class Config
691699
'auto_delete' => 2,
692700
'type' => 'string',
693701
],
702+
'cache_sqlite_wal' => [
703+
'text' => <<<'END'
704+
/**
705+
* @var bool
706+
*
707+
* This is only used for the SQLite3 cache system.
708+
* Whether to enable Write-Ahead Logging.
709+
*/
710+
END,
711+
'default' => false,
712+
'type' => 'boolean',
713+
],
694714
'image_proxy_enabled' => [
695715
'text' => <<<'END'
696716

0 commit comments

Comments
 (0)