1=head1 NAME 2 3DBIx::SQLEngine::Record::Cache - Avoid Repeated Selects 4 5=head1 SYNOPSIS 6 7B<Setup:> Several ways to create a class. 8 9 my $sqldb = DBIx::SQLEngine->new( ... ); 10 11 $class_name = $sqldb->record_class( $table_name, undef, 'Cache' ); 12 13 $sqldb->record_class( $table_name, 'My::Record', 'Cache' ); 14 15 package My::Record; 16 use DBIx::SQLEngine::Record::Class '-isasubclass', 'Cache'; 17 My::Record->table( $sqldb->table($table_name) ); 18 19B<Cache:> Uses Cache::Cache interface. 20 21 $class_name->use_cache_style('simple'); 22 23 # requires Cache::FastMemoryCache 24 $class_name->use_cache_style('active'); 25 26 use Cache::Cache; 27 $class_name->cache_cache( $my_cache_cache_object ); 28 29B<Basics:> Layered over superclass. 30 31 # Fetches from cache if it's been seen before 32 $record = $class_name->fetch_record( $primary_key ); 33 34 # Fetches from cache if we've run this query before 35 @records = $class_name->fetch_select(%clauses)->records; 36 37 # Clears cache so it's seen by next select query 38 $record->insert_record(); 39 40 # Clears cache so it's seen by next select query 41 $record->update_record(); 42 43 # Clears cache so it's seen by next select query 44 $record->delete_record(); 45 46 47=head1 DESCRIPTION 48 49This package provides a caching layer for DBIx::SQLEngine::Record objects. 50 51Don't use this module directly; instead, pass its name as a trait when you create a new record class. This package provides a multiply-composable collection of functionality for Record classes. It is combined with the base class and other traits by DBIx::SQLEngine::Record::Class. 52 53=cut 54 55######################################################################## 56 57package DBIx::SQLEngine::Record::Cache; 58 59use strict; 60use Carp; 61 62use Storable 'freeze'; 63 64######################################################################## 65 66######################################################################## 67 68=head1 CACHE INTERFACE 69 70=cut 71 72######################################################################## 73 74=head2 Cache Configuration 75 76=over 4 77 78=item cache_cache() 79 80 $record_class->cache_cache() : $cache_cache 81 $record_class->cache_cache( $cache_cache ) 82 83Gets or sets the cache object associated with this record class. 84 85If no cache has been set for a given class, no caching is performed. 86 87=back 88 89B<Cache Object Requirements:> This package in intended to work with cache object that use the Cache::Cache interface. However, any package which support the limited cache interface used by this package should be sufficient. 90 91Two small classes are included that support this interface; see L<DBIx::SQLEngine::Cache::TrivialCache> and L<DBIx::SQLEngine::Cache::BasicCache>. 92 93The following methods are used 94 95=over 4 96 97Constructor. 98 99=item get_namespace() 100 101Used to differentiate one cache object from another. 102 103=item get() 104 105Fetch a value from the cache, if it is present. 106 107=item set() 108 109Set a value in the cache. 110 111=item clear() 112 113Clear some or all values in the cache. 114 115=back 116 117=cut 118 119use Class::MakeMethods ( 120 'Template::ClassInherit:scalar' => 'cache_cache', 121); 122 123######################################################################## 124 125=head2 Cache Operations 126 127=over 4 128 129=item cache_key() 130 131 $record_class->cache_key( $key ) : $string_value 132 $record_class->cache_key( \@key ) : $string_value 133 $record_class->cache_key( \%key ) : $string_value 134 135Returns the string value to be used as a cache key. The argument may be an existing string, a reference to a shallow array whose elements will be joined with "\0/\0", or any other reference value which will be stringified by Storable. 136 137=item cache_get() 138 139 $record_class->cache_get( $key ) : $value 140 $record_class->cache_get( $key ) : ( $value, $updater_code_ref ) 141 142Returns the cached value associated with this key, if any. If called in a list context, also returns a reference to a subroutine which will save a new value for that key. 143 144=item cache_set() 145 146 $record_class->cache_set( $key, $value ) 147 148Caches this value under the provided key. 149 150=item cache_get_set() 151 152 $record_class->cache_get_set( $key, $code_ref, @args ) : $value 153 154Returns the curent value provided by a cache_get on the provided key, or if it is undefined, invokes the subroutine reference with any additional arguments provided, and saves the subroutine's return value as the cached value. 155 156=item cache_clear() 157 158 $record_class->cache_clear() 159 $record_class->cache_clear( $key ) 160 161Clear all values from the cache, or just those associated with the given key. 162 163=back 164 165=cut 166 167sub cache_key { 168 my ( $self, $key ) = @_; 169 my $type = ref($key); 170 if ( ! $type ) { 171 $key 172 } elsif ( $type eq 'ARRAY' ) { 173 join("\0/\0", @$key) 174 } else { 175 local $Storable::canonical = 1; 176 freeze($key) 177 } 178} 179 180# $value = $self->cache_get( $key ); 181# ( $value, $update ) = $self->cache_get( $key ); 182sub cache_get { 183 my ( $self, $key ) = @_; 184 185 my $cache = $self->cache_cache() or return; 186 187 $key = $self->cache_key($key) if ( ref $key ); 188 my $current = $cache->get( $key ); 189 190 if ( ! defined $current ) { 191 $self->cache_log_operation( $cache, 'miss', $key ); 192 } else { 193 $self->cache_log_operation( $cache, 'hit', $key ); 194 } 195 196 ! wantarray ? $current : ( $current, sub { 197 $self->cache_log_operation( $cache, 'update', $key ); 198 $cache->set( $key, @_ ); 199 } ); 200} 201 202# $self->cache_set( $key, $value ); 203sub cache_set { 204 my ( $self, $key, @value ) = @_; 205 206 my $cache = $self->cache_cache() or return; 207 208 $key = $self->cache_key($key) if ( ref $key ); 209 210 $self->cache_log_operation( $cache, 'write', $key ); 211 $cache->set( $key, @value ); 212} 213 214# $value = $self->cache_get_set( $key, \&sub, @args ); 215sub cache_get_set { 216 my ( $self, $key, $sub, @args ) = @_; 217 218 my ($current, $update) = $self->cache_get($key); 219 220 if ( ! defined $current ) { 221 $current = &$sub( @args ); 222 &$update( defined($current) ? $current : '' ); 223 } 224 225 $current; 226} 227 228# $self->cache_clear(); 229# $self->cache_clear( $key ); 230sub cache_clear { 231 my ( $self, $key ) = @_; 232 233 my $cache = $self->cache_cache() or return; 234 235 if ( ! $key ) { 236 $self->cache_log_operation( $cache, 'clear' ); 237 $cache->clear(); 238 } else { 239 $self->cache_log_operation( $cache, 'clear', $key ); 240 $cache->set($key, undef); 241 } 242} 243 244######################################################################## 245 246=head2 Cache Logging 247 248=over 4 249 250=item CacheLogging() 251 252 $record_class->CacheLogging() : $level 253 $record_class->CacheLogging( $level ) 254 255Sets the logging level associated with a given class. 256 257=item cache_log_operation() 258 259 $record_class->cache_log_operation( $cache, $operation, $key ) 260 261Does nothing unless a CacheLogging level is set for this class. 262 263Uses warn() to print a message to the error log, including the key string used, and the operation, which will be one of "hit", "miss", "write", and "clear". 264 265If the level is greater than one, the message will also include a history of prior operations on this key. 266 267=back 268 269=cut 270 271use Class::MakeMethods ( 272 'Template::ClassInherit:scalar' => 'CacheLogging', 273); 274 275use vars qw( %CachingHistory ); 276 277sub cache_log_operation { 278 my ( $self, $cache, $oper, $key ) = @_; 279 my $level = $self->CacheLogging() or return; 280 my $namespace = $cache->get_namespace; 281 if ( $level > 1 ) { 282 my $history = ( $CachingHistory{ $key } ||= [] ); 283 $oper .= " (" . join(' ', @$history ) . ")"; 284 push @$history, $oper; 285 } 286 warn "Cache $namespace: $oper " . DBIx::SQLEngine::printable($key) . "\n"; 287} 288 289######################################################################## 290 291=head2 Cache Styles 292 293=over 4 294 295=item define_cache_styles() 296 297 DBIx::SQLEngine->define_cache_styles( $name, $code_ref ) 298 DBIx::SQLEngine->define_cache_styles( %names_and_code_refs ) 299 300Define a named caching style. The code ref supplied for each name should create and return an object from the Cache::Cache hierarchy, or another caching class which supports the interface described in the "Cache Object Requirements" section above. 301 302=item cache_styles() 303 304 DBIx::SQLEngine->cache_styles() : %names_and_info 305 DBIx::SQLEngine->cache_styles( $name ) : $info 306 DBIx::SQLEngine->cache_styles( \@names ) : @info 307 DBIx::SQLEngine->cache_styles( $name, $info, ... ) 308 DBIx::SQLEngine->cache_styles( \%names_and_info ) 309 310Accessor for global hash mapping cache names to initialization subroutines. 311 312=item use_cache_style() 313 314 $class_name->use_cache_style( $cache_style_name ) 315 $class_name->use_cache_style( $cache_style_name, @options ) 316 317Uses the named caching definition to create a new cache object, and associates it with the given class. 318 319Use one of the predefined caching styles described in the "Default Caching Styles" section below, or define your own cache styles with define_cache_styles. 320 321=back 322 323=cut 324 325use Class::MakeMethods ( 326 'Standard::Global:hash' => 'cache_styles', 327); 328 329sub define_cache_styles { 330 my $self = shift; 331 $self->cache_styles( @_ ); 332} 333 334sub use_cache_style { 335 my ( $class, $style, %options ) = @_; 336 my $sub = $class->cache_styles( $style ); 337 my $cache = $sub->( $class, %options ); 338 $class->cache_cache( $cache ); 339} 340 341######################################################################## 342 343=pod 344 345B<Default Caching Styles:> The following cache styles are predefined. Except for 'simple', using any of these styles will require installation of the Cache::Cache distribution. 346 347=over 4 348 349=item 'simple' 350 351Uses DBIx::SQLEngine::Cache::TrivialCache. 352 353=item 'live' 354 355Uses Cache::FastMemoryCache with a default expiration time of 1 seconds. 356 357=item 'active' 358 359Uses Cache::FastMemoryCache with a default expiration time of 5 seconds. 360 361=item 'stable' 362 363Uses Cache::FastMemoryCache with a default expiration time of 30 seconds. 364 365=item 'file' 366 367Uses Cache::FileCache with a default expiration time of 30 seconds. 368 369=back 370 371B<Examples:> 372 373=over 2 374 375=item * 376 377 # requires DBIx::SQLEngine::Cache::TrivialCache 378 $class_name->use_cache_style('simple'); 379 380=item * 381 382 # requires Cache::FastMemoryCache from CPAN 383 $class_name->use_cache_style('active'); 384 385=back 386 387=cut 388 389__PACKAGE__->define_cache_styles( 390 'simple' => sub { 391 require DBIx::SQLEngine::Cache::TrivialCache; 392 DBIx::SQLEngine::Cache::TrivialCache->new(); 393 }, 394 'live' => sub { 395 require Cache::FastMemoryCache; 396 Cache::FastMemoryCache->new( { 397 'namespace' => 'RecordCache:' . (shift), 398 'default_expires_in' => 1, 399 'auto_purge_interval' => 10, 400 @_ 401 } ) 402 }, 403 'active' => sub { 404 require Cache::FastMemoryCache; 405 Cache::FastMemoryCache->new( { 406 'namespace' => 'RecordCache:' . (shift), 407 'default_expires_in' => 5, 408 'auto_purge_interval' => 60, 409 @_ 410 } ) 411 }, 412 'stable' => sub { 413 require Cache::FastMemoryCache; 414 Cache::FastMemoryCache->new( { 415 'namespace' => 'RecordCache:' . (shift), 416 'default_expires_in' => 30, 417 'auto_purge_interval' => 60, 418 @_ 419 } ) 420 }, 421 'file' => sub { 422 require Cache::FileCache; 423 Cache::FileCache->new( { 424 'namespace' => 'RecordCache:' . (shift), 425 'default_expires_in' => 30, 426 'auto_purge_interval' => 60, 427 @_ 428 } ) 429 }, 430); 431 432######################################################################## 433 434######################################################################## 435 436=head1 FETCHING DATA (SQL DQL) 437 438Each of these methods provides a cached version of the superclass method. 439The results of queries are cached based on the SQL statement and parameters used. 440 441=over 4 442 443=item fetch_select() 444 445 $class_name->fetch_select( %select_clauses ) : $record_set 446 447Retrives records from the table using the provided SQL select clauses. 448 449=item fetch_one_record() 450 451 $sqldb->fetch_one_record( %select_clauses ) : $record_hash 452 453Retrives one record from the table using the provided SQL select clauses. 454 455=item select_record() 456 457 $class_name->select_record ( $primary_key_value ) : $record_obj 458 $class_name->select_record ( \@compound_primary_key ) : $record_obj 459 $class_name->select_record ( \%hash_with_primary_key_value ) : $record_obj 460 461Fetches a single record by primary key. 462 463=item select_records() 464 465 $class_name->select_records ( @primary_key_values_or_hashrefs ) : $record_set 466 467Fetches a set of one or more records by primary key. 468 469=item visit_select() 470 471 $class_name->visit_select ( $sub_ref, %select_clauses ) : @results 472 $class_name->visit_select ( %select_clauses, $sub_ref ) : @results 473 474Calls the provided subroutine on each matching record as it is retrieved. Returns the accumulated results of each subroutine call (in list context). 475 476To Do: This could perform caching of the matched records, but currently does not. 477 478=back 479 480The conversion of select clauses to a SQL statement is performed by the sql_select method: 481 482=over 4 483 484=item sql_select() 485 486 $class_name->sql_select ( %sql_clauses ) : $sql_stmt, @params 487 488Uses the table to call the sql_select method on the current SQLEngine driver. 489 490=back 491 492=cut 493 494# $records = $record_class->fetch_select( %select_clauses ); 495sub fetch_select { 496 my $self = shift; 497 my %clauses = @_; 498 499 my @sql = $self->sql_select( %clauses ); 500 501 my ($records, $update) = $self->cache_get( \@sql ); 502 503 if ( ! defined $records ) { 504 $records = $self->NEXT('fetch_select', sql => \@sql ); 505 $update->( $records ) if ( $update and $records ); 506 } 507 508 return $records; 509} 510 511sub fetch_one_record { 512 local $SIG{__DIE__} = \&Carp::confess; 513 (shift)->fetch_select( @_, 'limit' => 1 )->record( 0 ) 514} 515 516# @results = $self->visit_select( %select_clauses, $sub ); 517sub visit_select { 518 my $self = shift; 519 my $sub = ( ref($_[0]) ? shift : pop ); 520 my %clauses = @_; 521 522 my @sql = $self->sql_select( %clauses ); 523 524 my ($records, $update) = $self->cache_get( \@sql ); 525 526 if ( $records ) { 527 return map &$sub( $_ ), @$records; 528 } 529 $self->sqlengine_do('visit_select', @_, $sub ) 530} 531 532######################################################################## 533 534sub sql_select { 535 (shift)->get_table->sqlengine_do( 'sql_select', @_ ); 536} 537 538######################################################################## 539 540=head2 Vivifying Records From The Database 541 542These methods are called internally by the various select methods and do not need to be called directly. 543 544=over 4 545 546=item record_from_db_data() 547 548 $class_name->record_from_db_data( $hash_ref ) 549 550Calls SUPER method, then cache_records(). 551 552=item record_set_from_db_data() 553 554 $class_name->record_set_from_db_data( $hash_array_ref ) 555 556Calls SUPER method, then cache_records(). 557 558=item cache_records() 559 560 $class_name->cache_records( @records ) 561 562Adds records to the cache. 563 564=back 565 566=cut 567 568# $record_class->record_from_db_data( $hash_ref ); 569sub record_from_db_data { 570 my $self = shift; 571 my $record = $self->NEXT('record_from_db_data', @_ ); 572 $self->cache_records( $record ); 573 $record; 574} 575 576# $record_class->record_set_from_db_data( $hash_array_ref ); 577sub record_set_from_db_data { 578 my $self = shift; 579 my $recordset = $self->NEXT('record_set_from_db_data', @_ ); 580 $self->cache_records( @$recordset ); 581 $recordset; 582} 583 584sub cache_records { 585 my $self = shift; 586 my $id_col = $self->column_primary_name(); 587 foreach my $record ( @_ ) { 588 my $tablename = $self->table->name; 589 my $criteria = { $id_col => $record->{ $id_col } }; 590 my %index = ( where => { $id_col => $record->{ $id_col } }, limit => 1, table => $self->table->name ); 591 $self->cache_set( \%index, DBIx::SQLEngine::Record::Set->new($record) ); 592 } 593} 594 595######################################################################## 596 597######################################################################## 598 599=head1 EDITING DATA (SQL DML) 600 601=head2 Insert to Add Records 602 603After constructing a record with one of the new_*() methods, you may save any changes by calling insert_record. 604 605=over 4 606 607=item insert_record 608 609 $record_obj->insert_record() : $flag 610 611Attempt to insert the record into the database. Calls SUPER method, so implemented using MIXIN. 612 613Clears the cache. 614 615=back 616 617=cut 618 619# $record->insert_record() 620sub insert_record { 621 my $self = shift; 622 $self->cache_clear(); 623 $self->NEXT('insert_record', @_ ); 624} 625 626######################################################################## 627 628=head2 Update to Change Records 629 630After retrieving a record with one of the fetch methods, you may save any changes by calling update_record. 631 632=over 4 633 634=item update_record 635 636 $record_obj->update_record() : $record_count 637 638Attempts to update the record using its primary key as a unique identifier. 639Calls SUPER method, so implemented using MIXIN. 640 641Clears the cache. 642 643=back 644 645=cut 646 647# $record->update_record() 648sub update_record { 649 my $self = shift; 650 $self->cache_clear(); 651 $self->NEXT('update_record', @_ ); 652} 653 654######################################################################## 655 656=head2 Delete to Remove Records 657 658=over 4 659 660=item delete_record() 661 662 $record_obj->delete_record() : $record_count 663 664Delete this existing record based on its primary key. 665Calls SUPER method, so implemented using MIXIN. 666 667Clears the cache. 668 669=back 670 671=cut 672 673# $record->delete_record() 674sub delete_record { 675 my $self = shift; 676 $self->cache_clear(); 677 $self->NEXT('delete_record', @_ ); 678} 679 680######################################################################## 681 682######################################################################## 683 684=head1 SEE ALSO 685 686For more about the Record classes, see L<DBIx::SQLEngine::Record::Class>. 687 688See L<DBIx::SQLEngine> for the overall interface and developer documentation. 689 690See L<DBIx::SQLEngine::Docs::ReadMe> for general information about 691this distribution, including installation and license information. 692 693=cut 694 695######################################################################## 696 6971; 698 699__END__ 700 701### DBO::Row::CachedRow 702 703### Change History 704 # 2000-12-29 Added table_or_die() for better debugging output. 705 # 2000-05-24 Adjusted fall-back behavior in fetch_sql. 706 # 2000-04-12 Check whether being called on instance or class before blessing. 707 # 2000-04-11 Fixed really anoying fetch_id problem. 708 # 2000-04-05 Completed expiration and pruning methods. 709 # 2000-04-04 Check for empty-string criteria, ordering in cache_key_for_fetch 710 # 2000-03-29 Fixed cache expiration for multi-row fetch. 711 # 2000-03-06 Touchups. 712 # 2000-01-13 Overhauled. -Simon 713 714######################################################################## 715 716 717 718 719 720######################################################################## 721 722# $rows = RowClass->fetch( $criteria, $order ) 723sub fetch { 724 my $self = shift; 725 726 return $self->query_cache->cache_get_set( 727 $self->cache_key_for_fetch( @_ ), 728 \&___cache_fetch, $self, @_ 729 ); 730} 731 732# $rows = RowClass->fetch_sql( $sql ) 733sub fetch_sql { 734 my $self = shift; 735 736 return $self->query_cache->cache_get_set( 737 join('__', @_), 738 \&___cache_fetch_sql, $self, @_ 739 ); 740} 741 742# $row = RowClass->fetch_id( $id ) 743sub fetch_id { 744 my $self = shift; 745 746 return $self->row_cache->cache_get_set( 747 join('__', @_), 748 \&___cache_fetch_id, $self, @_ 749 ); 750} 751 752######################################################################## 753 754sub insert_row { 755 my $row = shift; 756 757 $row->query_cache->clear_all() if ( $row->query_cache ); 758 759 my $id_col = $row->table_or_die()->id_column(); 760 my $row_cache = $row->row_cache; 761 if ( $id_col and $row_cache ) { 762 $row_cache->replace( $row->{$id_col}, $row ); 763 } 764 765 return $row->NEXT('insert_row', @_); 766} 767 768sub update_row { 769 my $row = shift; 770 $row->query_cache->clear_all() if ( $row->query_cache ); 771 return $row->NEXT('update_row', @_); 772} 773 774sub delete_row { 775 my $row = shift; 776 777 my $id_col = $row->table_or_die()->id_column(); 778 my $row_cache = $row->row_cache; 779 if ( $id_col and $row_cache ) { 780 $row_cache->clear( $row->{$id_col} ); 781 } 782 783 $row->query_cache->clear_all() if ( $row->query_cache ); 784 return $row->NEXT('delete_row, @_); 785} 786 7871; 788