Summary
When Providence 2.0.11 is configured with the SQLite cache backend, caUtils update-from-1-7 crashes during Step 1 of 9, Clearing caches, with:
PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database
The failure was reproduced using the official Providence 2.0.11 release. Filesystem permissions, ownership, ACLs, filesystem attributes, and SQLite read-only mode were checked and do not account for the error.
Environment
- Source installation: Providence 1.7.11
- Source database revision: 158
- Target: official Providence 2.0.11 release ZIP
- PHP CLI: 8.4
- OS: Rocky Linux 8
- Database: MariaDB 10.6
- Test context: isolated staging clone; production was unchanged
Configuration
define('__CA_CACHE_BACKEND__', 'sqlite');
The cache and temporary directory both resolve to app/tmp. The SQLite cache file resolves beneath:
app/tmp/collectiveaccessCache/cache.sqlite
Steps to reproduce
From the Providence 2.0.11 application directory:
php support/bin/caUtils update-from-1-7
Confirm the prompt to update the 1.7 database. The command begins Step 1, starts clearing application caches, and terminates with the SQLite exception before any MariaDB schema migration step is reached.
Expected behavior
The updater should clear every supported cache backend and continue to the database migration steps.
Actual behavior
The updater terminates in Stash\Driver\Sub\SqlitePdo::set() while application caches are being cleared. The representative call path is:
Stash\Driver\Sub\SqlitePdo->set()
Stash\Driver\Sqlite->storeData()
Stash\Item->executeSet()
Stash\Pool->save()
ExternalCache::save()
Configuration->__destruct()
MemoryCache::flush()
CLIUtils::clear_caches()
CLIUtils::update_from_1_7()
Reported source locations in the 2.0.11 package include:
vendor/tedivm/stash/src/Stash/Driver/Sub/SqlitePdo.php:152
app/lib/Cache/ExternalCache.php:159
app/lib/Configuration.php:1129
app/lib/Cache/MemoryCache.php:132
app/lib/Utils/CLIUtils/Maintenance.php:1080
app/lib/Utils/CLIUtils/Migration.php:61
support/bin/caUtils:180
Evidence against a permissions cause
- The effective PHP CLI user owns the cache file and parent directory.
- PHP reports both the SQLite file and cache directory as writable.
- The directory mode is
775; the SQLite file mode is 660.
- ACLs contain no restrictive entries.
- No immutable or append-only filesystem attributes are set.
- SQLite
PRAGMA query_only is 0.
- SQLite
PRAGMA journal_mode is delete.
- SQLite
PRAGMA locking_mode is normal.
Relevant source path
The 2.0.11 migration invokes CLIUtils::clear_caches() at the beginning of Step 1:
CLIUtils::clear_caches();
Source: Migration.php lines 56-62
The application-cache branch performs a full external-cache flush, followed by a memory-cache flush:
ExternalCache::flush();
MemoryCache::flush();
Source: Maintenance.php lines 1056-1081
For a full external-cache flush, Providence calls clear() on the existing static Stash pool but retains that pool in ExternalCache::$cache:
self::getCache()->clear();
Source: ExternalCache.php lines 200-208
Providence 2.0.11 locks tedivm/stash v1.2.2 at commit 39c426e. Its SQLite full-clear path closes the driver and deletes the SQLite file:
$this->driver = null;
$this->driver = false;
\Stash\Utilities::deleteRecursive($this->path);
Sources:
The observed call stack then reaches the configuration destructor, which writes cached configuration values through ExternalCache::save():
foreach(self::$s_config_cache as $k => $v) {
ExternalCache::save($k, $v, 'ConfigurationCache', 3600 * 3600 * 30);
}
Source: Configuration.php lines 1124-1132
Source analysis
The observed behavior and call path suggest a cache-lifecycle interaction: a full SQLite clear deletes the backing database, but a configuration destructor attempts another cache write during the same clear operation. I have not tested a code patch, so I cannot determine whether the safest correction belongs in Providence's pool lifecycle, its configuration-cache write lifecycle, or the bundled Stash driver.
Impact
- A 1.7-to-2.0 migration cannot proceed with this cache configuration.
- The exception resembles an ordinary permissions problem and can lead to unnecessary server changes.
- The failure occurs before the application database is migrated.
Confirmed workaround
Changing only the staging cache backend from sqlite to file, then moving the failed SQLite cache directory aside, allowed cache clearing and the migration to proceed. This was a staging-only workaround.
Possible implementation direction
One candidate would be to discard the retained static pool after a successful full clear, allowing the next cache access to initialize a new pool and SQLite driver:
self::getCache()->clear();
self::$cache = null;
That would be a small change in the full-clear branch of ExternalCache::flush(), but it is only a proposed direction. Maintainers may prefer to suppress configuration-cache writes during clearing or explicitly reinitialize the pool elsewhere in the lifecycle.
A regression test could configure __CA_CACHE_BACKEND__ = 'sqlite', populate the configuration cache, perform a full cache clear, release cached configuration objects, and then verify that a new cache write succeeds. An end-to-end update-from-1-7 test using SQLite caching would cover the reported path directly.
AI assistance disclosure
This report was developed with ChatGPT Work using GPT-5.6 Sol with Extra High reasoning for source review, troubleshooting support, and drafting. Runtime claims, where included, were reproduced on an isolated staging clone; source-level claims were checked against the official Providence 2.0.11 release code.
Summary
When Providence 2.0.11 is configured with the SQLite cache backend,
caUtils update-from-1-7crashes during Step 1 of 9,Clearing caches, with:The failure was reproduced using the official Providence 2.0.11 release. Filesystem permissions, ownership, ACLs, filesystem attributes, and SQLite read-only mode were checked and do not account for the error.
Environment
Configuration
The cache and temporary directory both resolve to
app/tmp. The SQLite cache file resolves beneath:Steps to reproduce
From the Providence 2.0.11 application directory:
Confirm the prompt to update the 1.7 database. The command begins Step 1, starts clearing application caches, and terminates with the SQLite exception before any MariaDB schema migration step is reached.
Expected behavior
The updater should clear every supported cache backend and continue to the database migration steps.
Actual behavior
The updater terminates in
Stash\Driver\Sub\SqlitePdo::set()while application caches are being cleared. The representative call path is:Reported source locations in the 2.0.11 package include:
Evidence against a permissions cause
775; the SQLite file mode is660.PRAGMA query_onlyis0.PRAGMA journal_modeisdelete.PRAGMA locking_modeisnormal.Relevant source path
The 2.0.11 migration invokes
CLIUtils::clear_caches()at the beginning of Step 1:CLIUtils::clear_caches();Source:
Migration.phplines 56-62The application-cache branch performs a full external-cache flush, followed by a memory-cache flush:
Source:
Maintenance.phplines 1056-1081For a full external-cache flush, Providence calls
clear()on the existing static Stash pool but retains that pool inExternalCache::$cache:Source:
ExternalCache.phplines 200-208Providence 2.0.11 locks
tedivm/stashv1.2.2 at commit39c426e. Its SQLite full-clear path closes the driver and deletes the SQLite file:Sources:
composer.locklines 10902-10907SqlitePdo.phplines 142-178The observed call stack then reaches the configuration destructor, which writes cached configuration values through
ExternalCache::save():Source:
Configuration.phplines 1124-1132Source analysis
The observed behavior and call path suggest a cache-lifecycle interaction: a full SQLite clear deletes the backing database, but a configuration destructor attempts another cache write during the same clear operation. I have not tested a code patch, so I cannot determine whether the safest correction belongs in Providence's pool lifecycle, its configuration-cache write lifecycle, or the bundled Stash driver.
Impact
Confirmed workaround
Changing only the staging cache backend from
sqlitetofile, then moving the failed SQLite cache directory aside, allowed cache clearing and the migration to proceed. This was a staging-only workaround.Possible implementation direction
One candidate would be to discard the retained static pool after a successful full clear, allowing the next cache access to initialize a new pool and SQLite driver:
That would be a small change in the full-clear branch of
ExternalCache::flush(), but it is only a proposed direction. Maintainers may prefer to suppress configuration-cache writes during clearing or explicitly reinitialize the pool elsewhere in the lifecycle.A regression test could configure
__CA_CACHE_BACKEND__ = 'sqlite', populate the configuration cache, perform a full cache clear, release cached configuration objects, and then verify that a new cache write succeeds. An end-to-endupdate-from-1-7test using SQLite caching would cover the reported path directly.AI assistance disclosure
This report was developed with ChatGPT Work using GPT-5.6 Sol with Extra High reasoning for source review, troubleshooting support, and drafting. Runtime claims, where included, were reproduced on an isolated staging clone; source-level claims were checked against the official Providence 2.0.11 release code.