package DBIx::Class::Storage::DBI::Replicated;
BEGIN {
- use Carp::Clan qw/^DBIx::Class/;
use DBIx::Class;
- croak('The following modules are required for Replication ' . DBIx::Class::Optional::Dependencies->req_missing_for ('replicated') )
+ die('The following modules are required for Replication ' . DBIx::Class::Optional::Dependencies->req_missing_for ('replicated') . "\n" )
unless DBIx::Class::Optional::Dependencies->req_ok_for ('replicated');
}
You can force a given query to use a particular storage using the search
attribute 'force_pool'. For example:
- my $RS = $schema->resultset('Source')->search(undef, {force_pool=>'master'});
+ my $rs = $schema->resultset('Source')->search(undef, {force_pool=>'master'});
-Now $RS will force everything (both reads and writes) to use whatever was setup
+Now $rs will force everything (both reads and writes) to use whatever was setup
as the master storage. 'master' is hardcoded to always point to the Master,
but you can also use any Replicant name. Please see:
L<DBIx::Class::Storage::DBI::Replicated::Pool> and the replicants attribute for more.
in a transaction when you are reading and writing to the same tables at the
same time, since your replicants will often lag a bit behind the master.
-See L<DBIx::Class::Storage::DBI::Replicated::Instructions> for more help and
-walkthroughs.
+If you have a multi-statement read only transaction you can force it to select
+a random server in the pool by:
+
+ my $rs = $schema->resultset('Source')->search( undef,
+ { force_pool => $db->storage->read_handler->next_storage }
+ );
=head1 DESCRIPTION
The following methods are delegated all the methods required for the
L<DBIx::Class::Storage::DBI> interface.
-=head2 read_handler
-
-Defines an object that implements the read side of L<BIx::Class::Storage::DBI>.
-
=cut
-has 'read_handler' => (
- is=>'rw',
- isa=>Object,
- lazy_build=>1,
- handles=>[qw/
- select
- select_single
- columns_info_for
- _dbh_columns_info_for
- _select
- /],
-);
-
-=head2 write_handler
-
-Defines an object that implements the write side of L<BIx::Class::Storage::DBI>,
-as well as methods that don't write or read that can be called on only one
-storage, methods that return a C<$dbh>, and any methods that don't make sense to
-run on a replicant.
-
-=cut
-
-has 'write_handler' => (
- is=>'ro',
- isa=>Object,
- lazy_build=>1,
- handles=>[qw/
+my $method_dispatch = {
+ writer => [qw/
on_connect_do
on_disconnect_do
on_connect_call
txn_commit
txn_rollback
txn_scope_guard
- sth
+ _exec_txn_rollback
+ _exec_txn_begin
+ _exec_txn_commit
deploy
with_deferred_fk_checks
dbh_do
- reload_row
- with_deferred_fk_checks
_prep_for_execute
-
- backup
is_datatype_numeric
_count_select
- _subq_update_delete
svp_rollback
svp_begin
svp_release
relname_to_table_alias
_dbh_last_insert_id
- _fix_bind_params
_default_dbi_connect_attributes
_dbi_connect_info
_dbic_connect_attributes
auto_savepoint
- _sqlt_version_ok
+ _query_start
_query_end
+ _format_for_trace
+ _dbi_attrs_for_bind
bind_attribute_by_data_type
transaction_depth
_dbh
_select_args
- _dbh_execute_array
+ _dbh_execute_for_fetch
_sql_maker
- _query_start
- _sqlt_version_error
- _per_row_update_delete
- _dbh_begin_work
_dbh_execute_inserts_with_no_binds
_select_args_to_query
+ _gen_sql_bind
_svp_generate_name
- _multipk_update_delete
- source_bind_attributes
_normalize_connect_info
_parse_connect_do
- _dbh_commit
- _execute_array
savepoints
- _sqlt_minimum_version
_sql_maker_opts
_conn_pid
- _conn_tid
_dbh_autocommit
_native_data_type
_get_dbh
sql_maker_class
- _dbh_rollback
- _adjust_select_args_for_complex_prefetch
- _resolve_ident_sources
- _resolve_column_info
- _prune_unused_joins
- _strip_cond_qualifiers
- _parse_order_by
- _resolve_aliastypes_from_select_args
_execute
_do_query
+ _sth
_dbh_sth
_dbh_execute
- _prefetch_insert_auto_nextvals
+ /, Class::MOP::Class->initialize('DBIx::Class::Storage::DBIHacks')->get_method_list ],
+ reader => [qw/
+ select
+ select_single
+ columns_info_for
+ _dbh_columns_info_for
+ _select
/],
-);
+ unimplemented => [qw/
+ _arm_global_destructor
+ _verify_pid
-my @unimplemented = qw(
- _arm_global_destructor
- _preserve_foreign_dbh
- _verify_pid
- _verify_tid
+ source_bind_attributes
+
+ get_use_dbms_capability
+ set_use_dbms_capability
+ get_dbms_capability
+ set_dbms_capability
+ _dbh_details
+ _dbh_get_info
- get_use_dbms_capability
- set_use_dbms_capability
- get_dbms_capability
- set_dbms_capability
+ sql_limit_dialect
+ sql_quote_char
+ sql_name_sep
- sql_limit_dialect
+ _prefetch_autovalues
- _dbh_details
+ _resolve_bindattrs
- _use_insert_returning
- _supports_insert_returning
+ _max_column_bytesize
+ _is_lob_type
+ _is_binary_lob_type
+ _is_text_lob_type
- _use_placeholders
- _supports_placeholders
- _determine_supports_placeholders
+ sth
+ /,(
+ # the capability framework
+ # not sure if CMOP->initialize does evil things to DBIC::S::DBI, fix if a problem
+ grep
+ { $_ =~ /^ _ (?: use | supports | determine_supports ) _ /x }
+ ( Class::MOP::Class->initialize('DBIx::Class::Storage::DBI')->get_all_method_names )
+ )],
+};
- _use_typeless_placeholders
- _supports_typeless_placeholders
- _determine_supports_typeless_placeholders
+if (DBIx::Class::_ENV_::DBICTEST) {
- _inner_join_to_node
-);
+ my $seen;
+ for my $type (keys %$method_dispatch) {
+ for (@{$method_dispatch->{$type}}) {
+ push @{$seen->{$_}}, $type;
+ }
+ }
+
+ if (my @dupes = grep { @{$seen->{$_}} > 1 } keys %$seen) {
+ die(join "\n", '',
+ 'The following methods show up multiple times in ::Storage::DBI::Replicated handlers:',
+ (map { "$_: " . (join ', ', @{$seen->{$_}}) } sort @dupes),
+ '',
+ );
+ }
-for my $method (@unimplemented) {
+ if (my @cant = grep { ! DBIx::Class::Storage::DBI->can($_) } keys %$seen) {
+ die(join "\n", '',
+ '::Storage::DBI::Replicated specifies handling of the following *NON EXISTING* ::Storage::DBI methods:',
+ @cant,
+ '',
+ );
+ }
+}
+
+for my $method (@{$method_dispatch->{unimplemented}}) {
__PACKAGE__->meta->add_method($method, sub {
- croak "$method must not be called on ".(blessed shift).' objects';
+ my $self = shift;
+ $self->throw_exception("$method must not be called on ".(blessed $self).' objects');
});
}
+=head2 read_handler
+
+Defines an object that implements the read side of L<BIx::Class::Storage::DBI>.
+
+=cut
+
+has 'read_handler' => (
+ is=>'rw',
+ isa=>Object,
+ lazy_build=>1,
+ handles=>$method_dispatch->{reader},
+);
+
+=head2 write_handler
+
+Defines an object that implements the write side of L<BIx::Class::Storage::DBI>,
+as well as methods that don't write or read that can be called on only one
+storage, methods that return a C<$dbh>, and any methods that don't make sense to
+run on a replicant.
+
+=cut
+
+has 'write_handler' => (
+ is=>'ro',
+ isa=>Object,
+ lazy_build=>1,
+ handles=>$method_dispatch->{writer},
+);
+
+
+
has _master_connect_info_opts =>
(is => 'rw', isa => HashRef, default => sub { {} });
around connect_info => sub {
my ($next, $self, $info, @extra) = @_;
- my $wantarray = wantarray;
-
my $merge = Hash::Merge->new('LEFT_PRECEDENT');
my %opts;
$merge->merge((delete $opts{pool_args} || {}), $self->pool_args)
);
- $self->pool($self->_build_pool)
- if $self->pool;
+ ## Since we possibly changed the pool_args, we need to clear the current
+ ## pool object so that next time it is used it will be rebuilt.
+ $self->clear_pool;
}
if (@opts{qw/balancer_type balancer_args/}) {
$self->_master_connect_info_opts(\%opts);
- my (@res, $res);
- if ($wantarray) {
+ my @res;
+ if (wantarray) {
@res = $self->$next($info, @extra);
} else {
- $res = $self->$next($info, @extra);
+ $res[0] = $self->$next($info, @extra);
}
# Make sure master is blessed into the correct class and apply role to it.
# link pool back to master
$self->pool->master($master);
- $wantarray ? @res : $res;
+ wantarray ? @res : $res[0];
};
=head1 METHODS
$self->read_handler($current);
};
- return $want_array ? @result : $result[0];
+ return wantarray ? @result : $result[0];
}
=head2 set_reliable_storage
foreach my $source ($self->all_storages) {
$source->limit_dialect(@_);
}
- return $self->master->quote_char;
+ return $self->master->limit_dialect;
}
=head2 quote_char