DBIx-Class-Async

 view release on metacpan or  search on metacpan

lib/DBIx/Class/Async/Storage.pm  view on Meta::CPAN

package DBIx::Class::Async::Storage;

$DBIx::Class::Async::Storage::VERSION   = '0.65';
$DBIx::Class::Async::Storage::AUTHORITY = 'cpan:MANWAR';

use strict;
use warnings;
use Scalar::Util qw(weaken);

=head1 NAME

DBIx::Class::Async::Storage - Storage Layer for DBIx::Class::Async

=head1 VERSION

Version 0.65

=cut

=head1 SYNOPSIS

    use DBIx::Class::Async::Storage;

    # Typically instantiated automatically via DBIx::Class::Async::Schema
    my $storage = $schema->storage;

    # Connection Management

    # Check if the worker pool is currently connected
    if ( $storage->connected ) {
        say "Worker pool is active and ready.";
    }

    # Gracefully shut down all background worker processes and disconnect
    $storage->disconnect;

    # Metadata & Configuration

    # Access the underlying schema instance
    my $schema = $storage->schema;

    # Inspect the DSN (Data Source Name) currently in use
    my $dsn = $storage->connect_info->[0];

    # Debugging

    # Storage acts as the gateway for the debug environment
    $storage->debug(1) if $ENV{DBIC_TRACE};

=head1 DESCRIPTION

C<DBIx::Class::Async::Storage> is the orchestration layer for the asynchronous
bridge. Unlike standard L<DBIx::Class::Storage::DBI>, this class does not
connect to the database directly. Instead, it manages the life-cycle of a
background worker pool.

=head1 CONSTRUCTOR

=head2 new

    my $storage = DBIx::Class::Async::Storage->new(
        schema   => $schema,
        async_db => $bridge_engine,
    );

B<async_db> is the internal engine (the 'bridge') that handles
communication with the worker processes.

This constructor automatically weakens the reference to the parent L<schema>
to prevent circular reference leaks, ensuring that worker processes are
correctly reaped when the schema object goes out of scope.

=cut

sub new {
    my ($class, %args) = @_;

    # Standard DBIC storage expects a reference to the schema
    my $self = bless {
        _schema   => $args{schema},
        _async_db => $args{async_db}, # The worker pool engine
    }, $class;

    # WEAKEN the schema reference to prevent circular memory leaks
    # that caused your "5 vs 2 processes" test failure.
    weaken($self->{_schema}) if $self->{_schema};

    return $self;
}

=head1 METHODS

=head2 cursor

  my $cursor = $storage->cursor($resultset);

Creates and returns a cursor object for iterating through a ResultSet's rows.

This is an abstract method that must be implemented by storage subclasses
(such as L<DBIx::Class::Async::Storage::DBI>). Calling this method directly
on the base storage class will throw an error.

B<Arguments>

=over 4

=item * C<$resultset> - A L<DBIx::Class::Async::ResultSet> object

=back

B<Returns>

A cursor object appropriate for the storage type. For DBI-based storage,
this returns a L<DBIx::Class::Async::Storage::DBI::Cursor> object.

  # Don't call on base class directly
  my $cursor = $storage->cursor($rs);  # Dies!

  # Use through a DBI storage subclass
  my $dbi_storage = DBIx::Class::Async::Storage::DBI->new(...);
  my $cursor = $dbi_storage->cursor($rs);  # Works!

Subclasses should override this method to return storage-specific cursor
implementations.

=cut

sub cursor {
    my $self = shift;
    die "Method 'cursor' must be implemented by a subclass of " . ref($self);
}

=head2 dbh

    my $dbh = $storage->dbh; # Returns undef

In this architecture, the Parent process B<must not> touch the database. This
method always returns C<undef>. Any code relying on C<$schema-E<gt>storage-E<gt>dbh>
will fail, which is intentional to prevent accidental blocking I/O in the
main event loop.

=cut

sub dbh {
    my $self = shift;
    # In Async mode, the parent process doesn't hold a DBH.
    return undef;
}

=head2 schema

  my $schema = $storage->schema;

Returns the L<DBIx::Class::Async::Schema> object that this storage layer
is associated with.

This provides a back-reference from the storage to its parent schema,
allowing storage components to access schema-level information and other
ResultSources when needed.

Note: The schema reference is weakened internally to prevent circular
reference memory leaks between the schema and storage objects.

  my $storage = $schema->storage;
  my $parent_schema = $storage->schema;

  # Access schema configuration
  my $source = $parent_schema->source('User');

=cut

sub schema {
    my $self = shift;
    return $self->{_schema};
}

=head2 disconnect

    $storage->disconnect;

Signals the background worker pool to shut down. This is an asynchronous-safe
cleanup:

1. It stops the worker processes.

2. It closes IPC pipes or sockets used by the bridge.

3. It clears the local query cache.

=cut

sub disconnect {
    my $self = shift;

    # Delegate cleanup to the functional library
    if ($self->{_async_db}) {
        DBIx::Class::Async::disconnect($self->{_async_db});
    }

    return 1;
}

=head1 INTERNAL NOTES

This class implements a specialised B<"Proxy Storage"> layer for the L<DBIx::Class::Async>
ecosystem. Unlike standard DBIC storage, this layer does not hold a local
database connection. Instead, it acts as a B<Command Dispatcher>.

B<Key Architectural Differences>:

=over 4

=item * Proxied Execution

SQL generation and execution occur within persistent background workers
via L<IO::Async::Function>.

=item * Asynchronous Transactions

Transactional integrity is maintained by routing related commands (C<txn_do>,
C<txn_batch>) to a single dedicated worker to ensure atomicity.



( run in 0.483 second using v1.01-cache-2.11-cpan-39bf76dae61 )