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_args

  • balancer_type / balancer_args

  • backend_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, ... }, reads user and password from 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 own dbh_maker inherits 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's user and password are 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.