NAME
DBIO::Replicated::Storage - Replicated DBI storage coordinator
VERSION
version 0.900004
SYNOPSIS
my $schema = MyApp::Schema->connect($master_dsn, $user, $pass, {
storage_type => '+DBIO::Replicated::Storage',
balancer_type => 'DBIO::Replicated::Balancer::First',
});
$schema->storage->connect_replicants(
[ $replica_dsn_1, $user, $pass ],
[ $replica_dsn_2, $user, $pass ],
);
# Temporarily force reads to the master
$schema->storage->execute_reliably(sub {
my $artist = $schema->resultset('Artist')->find(1);
...
});
See t/test/08_replicated.t for a runnable example.
DESCRIPTION
DBIO::Replicated::Storage coordinates one master backend plus zero or more replicant backends.
The master backend handles write-oriented work such as inserts, updates, deletes, transactions, deploy operations, and connection-time setup. Read-oriented methods are delegated to the current read handler, which is a balancer by default.
Replicants are managed through DBIO::Replicated::Pool. The pool can use replication hooks such as is_replicating and lag_behind_master when the underlying driver storage provides them.
The replicated layer consumes a small set of connect-info options for itself: pool_type, pool_args, balancer_type, balancer_args, and backend_storage_class. All remaining connect attributes are passed through to the master backend.
METHODS
new
my $storage = DBIO::Replicated::Storage->new($schema, \%args);
Normally created by the schema from storage_type. %args may set pool_type (default DBIO::Replicated::Pool), pool_args, balancer_type (default DBIO::Replicated::Balancer::First; a leading :: is expanded to DBIO::Replicated::Balancer::), balancer_args and backend_storage_class, the class every backend storage is built from: the storage class of the driver the schema declares, which "connection" in DBIO::Replicated passes here (ADR 0067). Left out, it is the generic DBIO::Storage::DBI, which has no driver and is refused at the first use of a backend. Each is also a plain read/write accessor of the same name. The same options are usually given in the connect info instead, see "connect_info".
master
The master DBIO::Replicated::Backend::Master, built on first use from backend_storage_class. Also a setter.
pool
The DBIO::Replicated::Pool of replicants, built on first use through "create_pool" with the master, backend_storage_class and pool_args. Also a setter.
balancer
The read balancer, built on first use through "create_balancer" with the pool, the master and balancer_args. Also a setter.
read_handler
The object every read method is sent to (see "DELEGATED METHODS"). Defaults to "balancer"; "set_reliable_storage" and "execute_reliably" point it at the master. Also a setter.
write_handler
The object every write method is sent to. Defaults to "master". Also a setter.
create_pool
create_balancer
my $pool = $storage->create_pool(%args);
my $balancer = $storage->create_balancer(%args);
Load pool_type / balancer_type and call its new with %args.
connect_info
Accepts the normal DBI connect-info arrayref used for the master connection.
Replicated-specific options can be supplied in connect-info hashrefs:
pool_type/pool_argsbalancer_type/balancer_argsbackend_storage_class
These options are consumed by the replicated layer. Remaining DBI attributes are passed through to the master backend and are also used as defaults when replicant connect info is merged later; "connect_replicants" says which.
connect_replicants
$storage->connect_replicants(
[ $dsn_1, $user, $pass ],
[ $dsn_2, $user, $pass, { RaiseError => 1 } ],
);
Connects one or more replicant backends and adds them to the pool.
Every replicant inherits the options the master was connected with -- its DBI attributes, on_connect_do / on_connect_call, quote_names and the other storage and SQL maker options -- merged into the replicant's connect-info hashref under its own, which win.
What names the master's server is never inherited: dsn, dbh_maker, dbname, host and port. A replicant is a different server; it says which in its own connect info.
The master's user and password (given when the master was connected in hash form, { dsn => ..., user => ..., password => ... }) depend on the replicant's form:
A replicant in hash form,
{ dsn => $dsn, ... }, readsuserandpasswordfrom its hash as its credentials. One that names none of its own inherits the master's; one with its own keeps them. One with its owndbh_makerinherits neither.A replicant in DBI form,
[ $dsn, $user, $pass, \%attrs ], connects with the credentials in its positions, or none. Its hash is its DBI attributes, so the master'suserandpasswordare never put there.
A replicant whose connection comes from a DBIO::AccessBroker is given as [ $broker ] or [ $broker, \%options ]. It gets the master's connect attributes the same way, in the hash beside its broker -- except dsn, user, password, dbh_maker, dbname, host and port: which database, and as whom, is its broker's answer ("connect_info" in DBIO::Storage::DBI).
replicants
The pool's hashref of replicant backends, keyed by name.
has_replicants
The number of replicants in the pool.
all_storages
The master followed by every replicant backend.
execute_reliably
$storage->execute_reliably(sub { ... });
Temporarily forces reads to use the master backend while the supplied coderef runs.
set_reliable_storage
Persistently switches the current read handler to the master backend.
set_balanced_storage
Restores the balancer as the current read handler after "set_reliable_storage".
connected
True when the master is connected and at least one replicant is.
ensure_connected
set_schema
disconnect
connect_call_datetime_setup
connect_call_rebase_sqlmaker
Run on every backend ("all_storages"); set_schema also sets the schema on this object.
debug
cursor_class
cursor
unsafe
disable_sth_caching
With an argument, set it on every backend; always return the master's value.
debugobj
debugfh
debugcb
Forwarded to the master only.
lag_behind_master
The largest lag_behind_master of all replicants, or nothing when there are none.
is_replicating
True when there is at least one replicant and every replicant reports is_replicating.
CAPABILITIES ARE THE MASTER'S
A capability (see DBIO::Storage::DBI::Capabilities) is a fact about the engine on the other end of a connection: either declared outright by the driver storage class (_use_X) or probed from the live $dbh and memoised per connection (_supports_X). A replicated storage has neither a driver class nor a handle of its own, so every capability question is answered by the master backend -- reads and writes both, _use_X and _supports_X alike:
# answers with the master's driver storage, not with the base-class default
$schema->storage->_use_supports_upsert;
# ... and a declaration lands on the master, where the answer is read from
$schema->storage->_use_supports_if_exists(1);
The master is the right authority because it owns DDL and the whole write path, and the engine whose version decides a capability is the one the master is connected to. This is the same reason sqlt_type and sql_maker are delegated to it.
The connection's namespace follows the same line: connection_schema -- the schema connect key, which reaches the master and every replicant -- is answered by the master backend, undef when the connection names none.
Mixed pools
The consequence is deliberate and worth stating outright: if the master and a replicant run engine versions that disagree about a capability, DBIO answers with the master's and never notices the difference. Nothing in this class reconciles the two, and nothing warns.
A pool whose members disagree about a capability is a misconfiguration, not a supported topology. A replicant is expected to be a replica of the master -- same engine, same major version, same feature set. When it is not, the divergence has to be resolved at the deployment level (upgrade the odd node out, or take it out of the pool), because a query planned against the master's capabilities can be dispatched to any replicant in the pool at any time.
DELEGATED METHODS
The storage methods below are not answered by this object. Called on an instance, each is forwarded to "write_handler" (the master) or "read_handler" (by default the DBIO::Replicated::Balancer, which picks a replicant, see "select" in DBIO::Replicated::Balancer); called on the class, the inherited DBIO::Storage::DBI method runs instead. See DBIO::Storage::DBI for what each does.
Sent to the master -- the write path, transactions, DDL, SQL generation and every capability question (see "CAPABILITIES ARE THE MASTER'S"):
- sql_maker
- sql_maker_class
- sqlt_type
- datetime_parser
- datetime_parser_type
- build_datetime_parser
- last_insert_id
- insert
- insert_bulk
- update
- delete
- dbh
- dbh_do
- txn_begin
- txn_do
- txn_commit
- txn_rollback
- txn_scope_guard
- transaction_depth
- deferred_rollback
- auto_savepoint
- savepoints
- svp_begin
- svp_release
- svp_rollback
- deploy
- with_deferred_fk_checks
- is_datatype_numeric
- relname_to_table_alias
- bind_attribute_by_data_type
- get_dbms_capability
- set_dbms_capability
- get_use_dbms_capability
- set_use_dbms_capability
- connection_schema
plus the private statement, connection and engine-fact hooks listed in @writer_methods in the source.
Sent to the read handler:
- select
- select_single
- columns_info_for
plus the private read paths _select, _dbh_columns_info_for, _execute_rendered, _explain_query and _prepare_rendered.
AUTHOR
DBIO & DBIx::Class Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.