Valkey 9.1 makes logical database access an ACL decision
Valkey 9.1 adds database permissions to ACL selectors and carries the checks through transactions and scripting. Its stable release also fixes memory-safety defects and spreads memory release during rehashing.
A user allowed to run GET does not necessarily need to read every logical database in a Valkey process. Valkey 9.1 adds that missing dimension to ACL policy. Database access joins command and key permissions, with checks that also cover operations crossing database boundaries.
Valkey 9.1.0 becomes stable on May 19. Its tagged release notes carry forward the candidate features and add three memory-safety security fixes. They also include incremental page release during hash-table rehashing.
I default to Valkey when an application needs Redis semantics. A familiar interface is useful, but it should not freeze the authorization model at the point where the project forked. Database-level permissions are a concrete improvement because they let policy describe an existing distinction in how applications organize keys.
Database rules belong to selectors
The database access-control change, introduced by Daniil Kashapov, adds database permissions to ACL selectors. The final ACL tests show explicit lists such as db=0,1, together with alldbs and resetdbs.
The database list is a permission condition, not an instruction to move a connection. A user restricted to database one still needs to select that database before accessing its keys. The tests distinguish commands that operate on data from keyless commands that remain usable while the connection's current database is inaccessible.
That distinction prevents an awkward authentication model in which a user could not perform the command needed to reach the database they are allowed to use. It also keeps data access checks relevant after authentication. Connecting successfully does not establish permission to read the current database.
Selectors can combine different rules. The tests include a user with write-oriented access in one database set and read-oriented access in another. Command permissions and database permissions are evaluated together within the corresponding selector. The database list does not independently grant every command.
This matters for application credentials that need several narrowly defined roles inside one server. A policy can describe where a command is allowed, with key patterns narrowing the permitted data further. It remains necessary to read the complete selector because a database allowance alone does not describe the user's authority.
The change preserves broad database access by default for compatibility. Existing users do not become restricted merely because the server now understands database rules. The compatibility tests explicitly exercise access across several databases after resetting a user. Restriction therefore requires an intentional policy change, which can be applied separately from installing the new server.
A short policy demonstrates the new dimension
The following example creates a fresh read-only user for database one and keys beginning with catalog:. It uses the database rule and ordinary command grants shown by the released ACL tests. The password is an illustrative value that must be replaced.
ACL SETUSER catalog-reader reset on >replace-this-password db=1 ~catalog:* +get +select +ping
ACL DRYRUN catalog-reader SELECT 1
ACL DRYRUN catalog-reader SELECT 2
The first dry run should succeed and the second should report lack of database permission. The user can read matching keys after selecting database one. PING permits a simple connection-level check without granting data modification.
Using reset here makes the example's starting state explicit. Updating an existing user without considering retained selectors could leave permissions that the short rule does not show. A credential policy is the resulting set of permissions, including any grants retained from earlier configuration.
The tests also establish a detail that can surprise configuration generators. Consecutive explicit database-list rules replace the selected list. A sequence ending in db=2 leaves database two as the allowed list, rather than accumulating every earlier value.
A generator that intends to grant several databases therefore needs to emit the intended list as one rule. The difference is observable through ACL GETUSER and ACL LIST, whose test cases include database-permission output. Those representations make the policy available for inspection after application.
Cross-database commands need more than a SELECT check
Checking SELECT alone would leave other ways to act on a different database. The released tests cover MOVE, SWAPDB and COPY with a destination database. A command that names a destination has to account for that database even if the connection never selects it.
FLUSHALL raises the same problem at a larger scope. A user with access to one database cannot acquire authority over every database merely by receiving broad command permissions. The tests expect a database permission error for FLUSHALL under that restricted policy, while allowing FLUSHDB in the permitted database.
This is a useful change in how logical database separation can be enforced. The permission system follows the operation's data scope. A command's name and the connection's current database are insufficient when the command affects additional databases.
The same reasoning applies to scripts. The test suite includes EVAL and stored-function cases that attempt to select a restricted database. They expect an ACL failure inside the script. Granting access to a scripting entry point does not remove the database restriction from the work it performs.
These checks make the feature more substantial than an additional configuration token. Database selection can occur through several execution paths, and authorization needs to survive those paths. The tests provide concrete examples of the intended boundary.
Transaction execution must respect policy changes
Transactions add another time at which a permission decision can become stale. The tests queue operations involving database selection, change the user's database policy before EXEC, and expect an authorization failure when the queued work executes.
That case matters because a queued command is not a permanent grant of authority. An administrator can alter the policy while work waits. The eventual execution needs to account for the permissions then in effect.
The suite also covers multiple SELECT commands within one transaction and the state after DISCARD. Those cases exercise how database context follows the queued command sequence. Checking only the connection's database at the start would miss context changes inside that sequence.
I find the test cases valuable because they expose the policy's actual semantics. The benefit to an application is a defined database authorization boundary. It is not a promise that logical databases now have independent process resources or separate failure domains.
An application sharing one server still needs a separate argument for resource and availability requirements. Database ACLs address access. Treating that improvement as a complete isolation architecture would assign it guarantees the change does not establish.
The stable release includes memory-safety maintenance
The stable notes list CVE-2026-23479 as a use-after-free in client unblocking. They also list invalid memory access in RESTORE under CVE-2026-25243 and a use-after-free during full synchronization while Lua or function execution yields under CVE-2026-23631.
Those descriptions identify distinct code paths. Their presence should not be collapsed into a claim that every installation has the same exposure. The release establishes that these fixes are included in the stable artifact. Installation-specific consequences require the conditions of the relevant advisory.
There is also a useful separation between the release's label and its contents. The notes describe this first stable 9.1 release with LOW upgrade urgency while listing security fixes. Reading only the urgency heading would omit material information in the security section.
For me, the meaningful change is that the stable branch combines the new authorization capability with maintenance of existing execution paths. Adopting database ACLs does not substitute for tracking memory-safety updates in the rest of the server.
Rehashing spreads the cost of releasing pages
The incremental memory-release patch addresses latency at the end of rehashing. Moving entries incrementally can still leave a large old table allocation to release when the transition finishes.
The patch dismisses ranges of old-table pages after their buckets have been processed. Its final revision batches sixteen pages per range. The Linux helper uses MADV_DONTNEED for an aligned address range, while the supplied helper has no equivalent action on other platforms.
That makes the mechanism more specific than a general claim of faster hashing. It spreads part of the memory-release work across progress already being made through the old table. The final allocation cleanup still exists, but the page-release work need not all arrive at that boundary.
The release therefore changes several different limits around ordinary application work. Database ACLs describe permitted scope, while incremental page release changes when memory housekeeping occurs. Their mechanisms are available in the tagged source and tests, so an application can adopt the policy feature without relying on a general performance claim to explain its behavior.