Committer: gariev
LJSUP-10124: [internal] Make user cluster DB switching dynamicallyUpdated ExtBlock - better local cache control, serialization support
U trunk/bin/upgrading/update-db-local.pl U trunk/cgi-bin/LJ/ExtBlock.pm U trunk/cgi-bin/LJ/Hooks/SUP.pm
Modified: trunk/bin/upgrading/update-db-local.pl =================================================================== --- trunk/bin/upgrading/update-db-local.pl 2011-10-12 18:20:21 UTC (rev 11086) +++ trunk/bin/upgrading/update-db-local.pl 2011-10-12 22:06:02 UTC (rev 11087) @@ -1019,6 +1019,8 @@ ( blockid varchar(20), blocktext blob, + last_updated INT UNSIGNED NOT NULL DEFAULT 0, + serialization char(1), -- default is NULL, 'J' = JSON, 'S' = Storable PRIMARY KEY (blockid) ) TYPE=InnoDB @@ -2139,6 +2141,11 @@ do_alter("sup_extblocks", "ALTER TABLE sup_extblocks ADD last_updated INT UNSIGNED NOT NULL DEFAULT 0"); } + + unless (column_type("sup_extblocks", "serialization")) { + do_alter("sup_extblocks", + "ALTER TABLE sup_extblocks ADD serialization CHAR(1)"); + } unless (column_type("userapps", "width")) { do_alter("userapps", qq{ Modified: trunk/cgi-bin/LJ/ExtBlock.pm =================================================================== --- trunk/cgi-bin/LJ/ExtBlock.pm 2011-10-12 18:20:21 UTC (rev 11086) +++ trunk/cgi-bin/LJ/ExtBlock.pm 2011-10-12 22:06:02 UTC (rev 11087) @@ -2,15 +2,66 @@ use strict; use warnings; +=head1 NAME + +LJ::ExtBlock - LJ interface to persistent store for everything + +Don't create a table each time you need to store something! + +=head1 SYNOPSYS + + use LJ::ExtBlock; + + LJ::ExtBlock->create_or_replace('foo', {"some" => "data"}); + + # ... later ... + if (my $block = LJ::ExtBlock->load_by_id('foo')) { + my $data = $block->data; + } + +=head1 METHODS + +=over + +=item $block = LJ::ExtBlock->create_or_replace($block_id, $data) + + +=item $block = LJ::ExtBlock->load_by_id($block_id, $options) + +Options (hashref): + +=over + +=item - skip_local_cache (boolean) - do not use in-process cache at all, reload everytime from memcache/db + +=item - cache_valid (number) - use local cache only if it's at most <number> seconds late, default is 5 mins. + +=back + + +=item $data = $block->data + + +=item $block->delete or LJ::ExtBlock->delete($block_id) + + +=back + +=cut + + use Carp qw/ croak confess /; +use LJ::JSON; +our %LOCAL_CACHE; ## block id --> [$object, time()] + ## ## Memcache routines ## use base 'LJ::MemCacheable'; *_memcache_id = \&blockid; sub _memcache_key_prefix { "sup_extblock" } -sub _memcache_stored_props { qw/ 1 blockid blocktext / } +sub _memcache_stored_props { qw/ 2 blockid blocktext last_updated serialization/ } *_memcache_hashref_to_object = \*_new; sub _memcache_expires { 3600 } @@ -24,6 +75,15 @@ my $params = shift || {}; confess "No object data" unless ref $params eq 'HASH'; my $self = bless $params, ref $class || $class; + + if (my $serialization = $self->{'serialization'}) { + if ($serialization eq 'J') { + $self->{'_data'} = LJ::JSON->from_json($self->{'blocktext'}); + } else { + die "Unknown serialization method: $serialization"; + } + } + return $self; } @@ -33,6 +93,10 @@ sub blockid { shift->{blockid} } sub blocktext { shift->{blocktext} } sub timestamp { shift->{last_updated} } +sub data { + my $self = shift; + return $self->{'_data'} || $self->{'blocktext'}; +} ## ## Class method. Returns a list (arrayref) of all known block IDs @@ -52,14 +116,18 @@ my $opts = shift || {}; ## 1. Find in local cache - if ($LJ::REQ_CACHE_EXT_BLOCK{$id} && !$opts->{'skip_local_cache'}) { - return $LJ::REQ_CACHE_EXT_BLOCK{$id}; + my $cache_valid = $opts->{'cache_valid'} || 300; + if (my $b = $LOCAL_CACHE{$id}) { + my ($ext_block, $cached_on) = @$b; + if (!$opts->{'skip_local_cache'} && ($cached_on + $cache_valid > time)) { + return $ext_block; + } } ## 2. Find in memcache my $self = $class->_load_from_memcache($id); if ($self) { - $LJ::REQ_CACHE_EXT_BLOCK{$id} = $self; + $LOCAL_CACHE{$id} = [$self, time()]; return $self; } @@ -73,7 +141,7 @@ $self = $class->_new($hash); $self->_store_to_memcache; - $LJ::REQ_CACHE_EXT_BLOCK{$id} = $self; + $LOCAL_CACHE{$id} = [$self, time()]; return $self; } @@ -85,16 +153,27 @@ my $id = shift; my $text = shift; + my $serialization; + if (ref $text) { + $serialization = 'J'; + $text = LJ::JSON->to_json($text); + } + my $dbh = LJ::get_db_writer(); - my $sth = $dbh->prepare("REPLACE INTO sup_extblocks (blockid, blocktext, last_updated) VALUES (?, ?, ?)"); + my $sth = $dbh->prepare("REPLACE INTO sup_extblocks (blockid, blocktext, last_updated, serialization) VALUES (?, ?, ?, ?)"); my $now = time; - $sth->execute($id, $text, $now) + $sth->execute($id, $text, $now, $serialization) or confess 'failed to replace into ext block with id: ' . $id . ', error = ' . $DBI::errstr; - my $self = $class->_new({ blockid => $id, blocktext => $text, last_updated => $now }); + my $self = $class->_new({ + blockid => $id, + blocktext => $text, + last_updated => $now, + serialization => $serialization, + }); $self->_store_to_memcache; - $LJ::REQ_CACHE_EXT_BLOCK{$id} = $self; + $LOCAL_CACHE{$id} = [$self, time()]; return $self; } @@ -107,7 +186,7 @@ my $id = shift; $id = $class->{blockid} if ref $class; # === if object - undef $LJ::REQ_CACHE_EXT_BLOCK{$id}; + undef $LOCAL_CACHE{$id}; $class->_remove_from_memcache($id); Modified: trunk/cgi-bin/LJ/Hooks/SUP.pm =================================================================== --- trunk/cgi-bin/LJ/Hooks/SUP.pm 2011-10-12 18:20:21 UTC (rev 11086) +++ trunk/cgi-bin/LJ/Hooks/SUP.pm 2011-10-12 22:06:02 UTC (rev 11087) @@ -324,7 +324,6 @@ LJ::register_hook('start_request', sub{ $LJ::SUP_REQUEST_ID = int(rand(1e9)); - %LJ::REQ_CACHE_EXT_BLOCK = (); @LJ::SUP_LJ_ENTRY_REQ = (); return; });