Committer: amyshkin
LJSUP-9804: Problem with writing properties data for entries on Omega/Alpha/Beta serversU trunk/bin/upgrading/update-db.pl U trunk/cgi-bin/LJ/Entry.pm U trunk/cgi-bin/LJ/User/PropStorage/DB.pm U trunk/cgi-bin/LJ/User/PropStorage.pm U trunk/cgi-bin/ljlib.pl
Modified: trunk/bin/upgrading/update-db.pl =================================================================== --- trunk/bin/upgrading/update-db.pl 2011-09-16 07:46:45 UTC (rev 20058) +++ trunk/bin/upgrading/update-db.pl 2011-09-16 08:26:11 UTC (rev 20059) @@ -12,21 +12,25 @@ use File::Basename (); use File::Copy (); use Image::Size (); -BEGIN { require "ljlib.pl"; - require "ljviews.pl"; } + +BEGIN { + require "ljlib.pl"; + require "ljviews.pl"; +} + use LJ::S2; use MogileFS::Admin; -my $opt_sql = 0; -my $opt_drop = 0; -my $opt_pop = 0; +my $opt_sql = 0; +my $opt_drop = 0; +my $opt_pop = 0; my $opt_confirm = ""; -my $opt_skip = ""; -my $opt_help = 0; -my $cluster = 0; # by default, upgrade master. +my $opt_skip = ""; +my $opt_help = 0; +my $cluster = 0; # by default, upgrade master. my $opt_listtables; my $opt_nostyles; -my $opt_forcebuild = 0; +my $opt_forcebuild = 0; my $opt_compiletodisk = 0; my $opt_innodb; my $opt_force_production_alter; @@ -44,7 +48,7 @@ -l --listtables Print used tables, one per line. --nostyles When used in combination with --populate, disables population of style information. - --force-alter By default, alter statements on production database are not + --force-alter By default, alter statements on production database are not executed (they may take too much time). This option forces these statements to be executed -b --beta Affects '--populate' option - only data that are safe during @@ -53,107 +57,121 @@ "; die $usage unless -GetOptions("runsql" => \$opt_sql, - "drop" => \$opt_drop, - "populate" => \$opt_pop, - "beta!" => \$opt_beta, - "confirm=s" => \$opt_confirm, - "cluster=s" => \$cluster, - "skip=s" => \$opt_skip, - "help" => \$opt_help, - "listtables" => \$opt_listtables, - "nostyles" => \$opt_nostyles, - "forcebuild|fb" => \$opt_forcebuild, - "ctd" => \$opt_compiletodisk, - "innodb" => \$opt_innodb, - "force-alter"=> \$opt_force_production_alter, - ); + GetOptions( + "runsql" => \$opt_sql, + "drop" => \$opt_drop, + "populate" => \$opt_pop, + "beta!" => \$opt_beta, + "confirm=s" => \$opt_confirm, + "cluster=s" => \$cluster, + "skip=s" => \$opt_skip, + "help" => \$opt_help, + "listtables" => \$opt_listtables, + "nostyles" => \$opt_nostyles, + "forcebuild|fb" => \$opt_forcebuild, + "ctd" => \$opt_compiletodisk, + "innodb" => \$opt_innodb, + "force-alter" => \$opt_force_production_alter, + ); $opt_nostyles = 1 unless LJ::is_enabled("update_styles"); -$opt_innodb = 1 if $LJ::USE_INNODB; +$opt_innodb = 1 if $LJ::USE_INNODB; die $usage if $opt_help; -die "You must specify '--populate' option with '--[no]beta'" +die "You must specify '--populate' option with '--[no]beta'" if defined $opt_beta && !$opt_pop; die "You are running script on beta server without specifying '--beta' option" if $opt_pop && !defined($opt_beta) && $LJ::IS_LJCOM_BETA; ## make sure $LJHOME is set so we can load & run everything -unless (-d $ENV{'LJHOME'}) { - die "LJHOME environment variable is not set, or is not a directory.\n". +unless ( -d $ENV{'LJHOME'} ) { + die "LJHOME environment variable is not set, or is not a directory.\n" . "You must fix this before you can run this database update script."; } die "Can't --populate a cluster" if $opt_pop && ($cluster && $cluster ne "all"); my @clusters; -foreach my $cl (split(/,/, $cluster)) { + +foreach my $cl ( split(/,/, $cluster) ) { die "Invalid cluster spec: $cl\n" unless $cl =~ /^\s*((\d+)|all|user)\s*$/; - if ($cl eq "all") { push @clusters, 0, @LJ::CLUSTERS; } - elsif ($cl eq "user") { push @clusters, @LJ::CLUSTERS; } - else { push @clusters, $1; } + + if ( $cl eq "all" ) { + push @clusters, 0, @LJ::CLUSTERS; + } + elsif ( $cl eq "user" ) { + push @clusters, @LJ::CLUSTERS; + } + else { + push @clusters, $1; + } } + @clusters = (0) unless @clusters; -my $su; # system user, not available until populate mode -my %status; # clusterid -> string -my %clustered_table; # $table -> 1 +my $su; # system user, not available until populate mode +my %status; # clusterid -> string +my %clustered_table = (); # $table -> 1 +my %table_exists = (); # $table -> 1 +my %table_unknown = (); # $table -> 1 +my %table_create = (); # $table -> $create_sql +my %table_drop = (); # $table -> 1 +my %table_status = (); # $table -> { SHOW TABLE STATUS ... row } +my %post_create = (); # $table -> [ [ $action, $what ]* ] +my %coltype = (); # $table -> { $col -> [ $type, $null ] } +my %indexname = (); # $table -> "INDEX"|"UNIQUE" . ":" . "col1-col2-col3" -> "PRIMARY" | index_name +my @alters = (); +my $dbh; my $sth; -my %table_exists; # $table -> 1 -my %table_unknown; # $table -> 1 -my %table_create; # $table -> $create_sql -my %table_drop; # $table -> 1 -my %table_status; # $table -> { SHOW TABLE STATUS ... row } -my %post_create; # $table -> [ [ $action, $what ]* ] -my %coltype; # $table -> { $col -> [ $type, $null ] } -my %indexname; # $table -> "INDEX"|"UNIQUE" . ":" . "col1-col2-col3" -> "PRIMARY" | index_name -my @alters; -my $dbh; -CLUSTER: foreach my $cluster (@clusters) { +CLUSTER: foreach my $cluster ( @clusters ) { print "Updating cluster: $cluster\n" unless $opt_listtables; + ## make sure we can connect $dbh = $cluster ? LJ::get_cluster_master($cluster) : LJ::get_db_writer(); - unless ($dbh) { - $status{$cluster} = "ERROR: Can't connect to the database (clust\#$cluster), so I can't update it. (".DBI->errstr.")"; + + unless ( $dbh ) { + $status{$cluster} = "ERROR: Can't connect to the database (clust\#$cluster), so I can't update it. (" . DBI->errstr . ")"; next CLUSTER; } $dbh->{'RaiseError'} = 0; $dbh->{'PrintError'} = 1 if $LJ::IS_DEV_SERVER; - # reset everything - %clustered_table = %table_exists = %table_unknown = - %table_create = %table_drop = %post_create = - %coltype = %indexname = %table_status = (); - @alters = (); - ## figure out what tables already exist (but not details of their structure) $sth = $dbh->prepare("SHOW TABLES"); $sth->execute; + while (my ($table) = $sth->fetchrow_array) { next if $table =~ /^(access|errors)\d+$/; $table_exists{$table} = 1; } - %table_unknown = %table_exists; # for now, later we'll delete from table_unknown + # for now, later we'll delete from table_unknown + %table_unknown = %table_exists; + ## very important that local is run first! (it can define tables that ## the site-wide would drop if it didn't know about them already) my $load_datfile = sub { - my $file = shift; - my $local = shift; + my ( $file, $local ) = @_; + return if $local && ! -e $file; + open(F, $file) or die "Can't find database update file at $file\n"; my $data; + { local $/ = undef; $data = <F>; } + close F; + eval $data; + die "Can't run $file: $@\n" if $@; return 1; }; @@ -162,38 +180,35 @@ $load_datfile->("$LJ::HOME/bin/upgrading/update-db-int.pl", 1); $load_datfile->("$LJ::HOME/bin/upgrading/update-db-general.pl"); - foreach my $t (sort keys %table_create) { + foreach my $t ( sort keys %table_create ) { delete $table_drop{$t} if ($table_drop{$t}); print "$t\n" if $opt_listtables; } + exit if $opt_listtables; - foreach my $t (keys %table_drop) { + foreach my $t ( keys %table_drop ) { delete $table_unknown{$t}; } - foreach my $t (keys %table_unknown) - { + foreach my $t ( keys %table_unknown ) { print "# Warning: unknown live table: $t\n"; } ## create tables - foreach my $t (keys %table_create) - { + foreach my $t ( keys %table_create ) { next if $table_exists{$t}; create_table($t); } ## drop tables - foreach my $t (keys %table_drop) - { + foreach my $t ( keys %table_drop ) { next unless $table_exists{$t}; drop_table($t); } ## do all the alters - foreach my $s (@alters) - { + foreach my $s ( @alters ) { $s->($dbh, $opt_sql); } @@ -201,12 +216,13 @@ } print "\ncluster: status\n"; + foreach my $clid (sort { $a <=> $b } keys %status) { printf "%7d: %s\n", $clid, $status{$clid}; } print "\n"; -if ($opt_pop) { +if ( $opt_pop ) { $dbh = LJ::get_db_writer() or die "Couldn't get master handle for population."; $dbh->{'RaiseError'} = 0; @@ -220,10 +236,11 @@ # Note: now cluster 0 means expunged (as well as statuvis 'X'), so there's # an option to disable the warning if you're running new code and know what's up. # if they're running modern code (with dversion 6 users), we won't check -if (table_exists('user')) { - unless ($dbh->selectrow_array("SELECT userid FROM user WHERE dversion >= 6 LIMIT 1")) { +if ( table_exists('user') ) { + unless ( $dbh->selectrow_array("SELECT userid FROM user WHERE dversion >= 6 LIMIT 1") ) { my $cluster0 = $dbh->selectrow_array("SELECT COUNT(*) FROM user WHERE clusterid=0"); - if ($cluster0) { + + if ( $cluster0 ) { print "\n", "* "x35, "\nWARNING: You have $cluster0 users on cluster 0.\n\n". "Support for that old database schema is deprecated and will be removed soon.\n". "You should stop updating from CVS until you've moved all your users to a cluster \n". @@ -251,6 +268,7 @@ # check for old style external_foaf_url (indexed:1, cldversion:0) my $prop = LJ::get_prop('user', 'external_foaf_url'); + if ($prop->{indexed} == 1 && $prop->{cldversion} == 0) { print "Updating external_foaf_url userprop.\n"; system("$ENV{'LJHOME'}/bin/upgrading/migrate-userprop.pl", 'external_foaf_url'); @@ -261,11 +279,12 @@ populate_proplists(); clean_schema_docs(); populate_mogile_conf(); - + unless ($opt_beta) { schema_upgrade_scripts(); populate_moods(); } + print "\nThe system user was created with a random password.\nRun \$LJHOME/bin/upgrading/make_system.pl to change its password and grant the necessary privileges." if $made_system; @@ -277,16 +296,21 @@ sub vivify_system_user { my $freshly_made = 0; my $su = LJ::load_user("system"); + unless ($su) { print "System user not found. Creating with random password.\n"; my $pass = LJ::make_auth_code(10); - LJ::create_account({ 'user' => 'system', - 'name' => 'System Account', - 'password' => $pass }) - || die "Failed to create system user."; + + LJ::create_account({ + 'user' => 'system', + 'name' => 'System Account', + 'password' => $pass, + }) || die "Failed to create system user."; + $su = LJ::load_user("system") || die "Failed to load the newly created system user."; $freshly_made = 1; } + return wantarray ? ($su, $freshly_made) : $su; } @@ -295,8 +319,8 @@ print "Populating public system styles (S1):\n"; require "$ENV{'LJHOME'}/bin/upgrading/s1style-rw.pl"; my $ss = s1styles_read(); + foreach my $uniq (sort keys %$ss) { - my $s = $ss->{$uniq}; my $existing = LJ::S1::check_dup_style($su, $s->{'type'}, $s->{'styledes'}); @@ -305,17 +329,25 @@ if ($LJ::DONT_TOUCH_STYLES) { next; } + if ( LJ::S1::update_style($existing->{'styleid'}, { map { $_, $s->{$_} } qw(formatdata is_embedded is_colorfree) }) ) { print " $uniq: "; print "updated \#$existing->{'styleid'}\n"; } + next; } # insert new - my %opts = ( "is_public" => 'Y', "opt_cache" => 'Y', - map { $_, $s->{$_} } qw(styledes type formatdata is_embedded is_colorfree lastupdate)); + my %opts = ( + "is_public" => 'Y', + "opt_cache" => 'Y', + map { + $_ => $s->{$_} + } qw(styledes type formatdata is_embedded is_colorfree lastupdate), + ); + LJ::S1::create_style($su, \%opts) or die "Error: unable to create style! Database potentially unavailable?"; print " $uniq: "; @@ -329,15 +361,16 @@ sub populate_s2 { # S2 print "Populating public system styles (S2):\n"; + { - my $LD = "s2layers"; # layers dir - + # layers dir + my $LD = "s2layers"; my $sysid = $su->{'userid'}; # find existing re-distributed layers that are in the database # and their styleids. my $existing = LJ::S2::get_public_layers({ force => 1 }, $sysid); - + my %known_id; chdir "$ENV{'LJHOME'}/bin/upgrading" or die; my %layer; # maps redist_uniq -> { 'type', 'parent' (uniq), 'id' (s2lid) } @@ -350,35 +383,38 @@ unless ($id) { my $parentid = 0; $parentid = $layer{$parent}->{'id'} unless $type eq "core"; + # allocate a new one. $dbh->do("INSERT INTO s2layers (s2lid, b2lid, userid, type) ". "VALUES (NULL, $parentid, $sysid, ?)", undef, $type); die $dbh->errstr if $dbh->err; $id = $dbh->{'mysql_insertid'}; + if ($id) { $dbh->do("INSERT INTO s2info (s2lid, infokey, value) VALUES (?,'redist_uniq',?)", undef, $id, $base); } } + die "Can't generate ID for '$base'" unless $id; # remember it so we don't delete it later. $known_id{$id} = 1; - + $existing->{$base}->{'s2lid'} = $id; $layer{$base} = { - 'type' => $type, + 'type' => $type, 'parent' => $parent, - 'id' => $id, + 'id' => $id, }; my $parid = $layer{$parent}->{'id'}; # see if source changed - my $md5_source = Digest::MD5::md5_hex($s2source); + my $md5_source = Digest::MD5::md5_hex($s2source); my $source_exist = LJ::S2::load_layer_source($id); - my $md5_exist = Digest::MD5::md5_hex($source_exist); + my $md5_exist = Digest::MD5::md5_hex($source_exist); # skip compilation if source is unchanged and parent wasn't rebuilt. return if $md5_source eq $md5_exist && ! $layer{$parent}->{'built'} && ! $opt_forcebuild; @@ -390,14 +426,15 @@ # we're going to go ahead and build it. $layer{$base}->{'built'} = 1; - unless ($dry) { + unless ( $dry ) { # compile! my $lay = { - 's2lid' => $id, + 's2lid' => $id, 'userid' => $sysid, - 'b2lid' => $parid, - 'type' => $type, + 'b2lid' => $parid, + 'type' => $type, }; + my $error = ""; my $compiled; my $info; @@ -407,11 +444,14 @@ # an error itself, which we can get. eval { die $error unless - LJ::S2::layer_compile($lay, \$error, { - 's2ref' => \$s2source, - 'redist_uniq' => $base, - 'compiledref' => \$compiled, - 'layerinfo' => \$info, + LJ::S2::layer_compile( + $lay, + \$error, + { + 's2ref' => \$s2source, + 'redist_uniq' => $base, + 'compiledref' => \$compiled, + 'layerinfo' => \$info, }); }; @@ -421,7 +461,7 @@ } if ($opt_compiletodisk) { - open (CO, ">$LD/$base.pl") or die; + open( CO, '>', "$LD/$base.pl" ) or die; print CO $compiled; close CO; } @@ -432,22 +472,23 @@ }; my @to_compile; + my $dry_compile = sub { my @args = @_; - $compile->(@args, 1); push @to_compile, \@args; }; my @layerfiles = ("s2layers.dat"); - while (@layerfiles) - { + + while ( @layerfiles ) { my $file = shift @layerfiles; next unless -e $file; - open (SL, $file) or die; + + open (SL, '<', $file) or die; print "SOURCE: $file\n"; - while (<SL>) - { + + while (<SL>) { s/\#.*//; s/^\s+//; s/\s+$//; next unless /\S/; my ($base, $type, $parent) = split; @@ -466,34 +507,44 @@ my $multi = ($type =~ s/\+$//); my $s2source; - open (L, "$LD/$base.s2") or die "Can't open file: $base.s2\n"; + open (L, '<', "$LD/$base.s2") or die "Can't open file: $base.s2\n"; unless ($multi) { # check if this layer should be mapped to another layer (i.e. exact copy except for layerinfo) if ($type =~ s/\(([^)]+)\)//) { # grab the layer in the parentheses and erase it open (my $map_layout, "$LD/$1.s2") or die "Can't open file: $1.s2\n"; + while (<$map_layout>) { $s2source .= $_; } } + while (<L>) { $s2source .= $_; } + $dry_compile->($base, $type, $parent, $s2source); - } else { + } + else { my $curname; + while (<L>) { if (/^\#NEWLAYER:\s*(\S+)/) { my $newname = $1; $dry_compile->($curname, $type, $parent, $s2source); - $curname = $newname; + $curname = $newname; $s2source = ""; - } elsif (/^\#NEWLAYER/) { + } + elsif (/^\#NEWLAYER/) { die "Badly formatted \#NEWLAYER line"; - } else { + } + else { $s2source .= $_; } } + $dry_compile->($curname, $type, $parent, $s2source); } + close L; } + close SL; } @@ -502,9 +553,11 @@ LJ::end_request(); my $pid = fork; + if ($pid) { waitpid($pid, 0); - } else { + } + else { $compile->(@$_) foreach @batch; exit 0; } @@ -521,33 +574,35 @@ $dbh->{'RaiseError'} = 0; $dbh->{'PrintError'} = 1 if $LJ::IS_DEV_SERVER; - if ($LJ::IS_DEV_SERVER) { - # now, delete any system layers that don't below (from previous imports?) - my @del_ids; - my $sth = $dbh->prepare("SELECT s2lid FROM s2layers WHERE userid=?"); - $sth->execute($sysid); - while (my $id = $sth->fetchrow_array) { - next if $known_id{$id}; - push @del_ids, $id; - } + if ($LJ::IS_DEV_SERVER) { + # now, delete any system layers that don't below (from previous imports?) + my @del_ids; + my $sth = $dbh->prepare("SELECT s2lid FROM s2layers WHERE userid=?"); + $sth->execute($sysid); - # if we need to delete things, prompt before blowing away system layers - if (@del_ids) { - print "\nWARNING: The following S2 layer ids are known as system layers but are no longer\n" . - "present in the import files. If this is expected and you really want to DELETE\n" . - "these layers, type 'YES' (in all capitals).\n\nType YES to delete layers " . - join(', ', @del_ids) . ": "; - my $inp = <STDIN>; - if ($inp =~ /^YES$/) { - print "\nOkay, I am PERMANENTLY DELETING the layers.\n"; - LJ::S2::delete_layer($_) foreach @del_ids; - } else { - print "\nOkay, I am NOT deleting the layers.\n"; + while (my $id = $sth->fetchrow_array) { + next if $known_id{$id}; + push @del_ids, $id; } + + # if we need to delete things, prompt before blowing away system layers + if (@del_ids) { + print "\nWARNING: The following S2 layer ids are known as system layers but are no longer\n" . + "present in the import files. If this is expected and you really want to DELETE\n" . + "these layers, type 'YES' (in all capitals).\n\nType YES to delete layers " . + join(', ', @del_ids) . ": "; + my $inp = <STDIN>; + + if ($inp =~ /^YES$/) { + print "\nOkay, I am PERMANENTLY DELETING the layers.\n"; + LJ::S2::delete_layer($_) foreach @del_ids; + } + else { + print "\nOkay, I am NOT deleting the layers.\n"; + } + } } } - - } } sub populate_schools { @@ -559,12 +614,14 @@ # show message about schools if ($sid) { print "Skipping school data population -- manual population of new data required.\n"; + } # okay to populate - } elsif (open(F, "$ENV{LJHOME}/bin/upgrading/schools.dat")) { + elsif (open(F, "$ENV{LJHOME}/bin/upgrading/schools.dat")) { print "Populating school data.\n"; my $sid; + while (<F>) { chomp; @@ -591,14 +648,19 @@ foreach my $file ("base-data.sql", "base-data-local.sql") { my $ffile = "$ENV{'LJHOME'}/bin/upgrading/$file"; next unless -e $ffile; + print "Populating database with $file.\n"; - open (BD, $ffile) or die "Can't open $file file\n"; - while (my $q = <BD>) - { - chomp $q; # remove newline + open (BD, '<', $ffile) or die "Can't open $file file\n"; + + while (my $q = <BD>) { + # remove newline + chomp $q; next unless ($q =~ /^(REPLACE|INSERT|UPDATE)/); - chop $q; # remove semicolon + + # remove semicolon + chop $q; $dbh->do($q); + if ($dbh->err) { print "$q\n"; die "# ERROR: " . $dbh->errstr . "\n"; @@ -609,12 +671,16 @@ } sub populate_proplists { - foreach my $file ("proplists.dat", "proplists-local.dat") { + foreach my $file ( "proplists.dat", "proplists-local.dat" ) { my $ffile = "$ENV{'LJHOME'}/bin/upgrading/$file"; next unless -e $ffile; + my $scope = ($file =~ /local/) ? "local" : "general"; populate_proplist_file($ffile, $scope); } + + LJ::MemCache::delete('CACHE_PROPID'); + LJ::MemCache::delete('CACHE_PROP'); } sub populate_proplist_file { @@ -622,23 +688,23 @@ open (my $fh, $file) or die "Failed to open $file: $!"; my %pk = ( - 'userproplist' => 'name', - 'logproplist' => 'name', - 'talkproplist' => 'name', - 'usermsgproplist' => 'name', - 'pollproplist2' => 'name', - 'categoryproplist' => 'name', - 'ratelist' => 'name', - ); + 'userproplist' => 'name', + 'logproplist' => 'name', + 'talkproplist' => 'name', + 'usermsgproplist' => 'name', + 'pollproplist2' => 'name', + 'categoryproplist' => 'name', + 'ratelist' => 'name', + ); my %propid_field = ( - 'categoryproplist' => 'propid', - 'logproplist' => 'propid', - 'pollproplist2' => 'propid', - 'ratelist' => 'rlid', - 'talkproplist' => 'tpropid', - 'usermsgproplist' => 'propid', - 'userproplist' => 'upropid', + 'categoryproplist' => 'propid', + 'logproplist' => 'propid', + 'pollproplist2' => 'propid', + 'ratelist' => 'rlid', + 'talkproplist' => 'tpropid', + 'usermsgproplist' => 'propid', + 'userproplist' => 'upropid', ); my %noscope = ( @@ -649,20 +715,24 @@ my $pk; # table's primary key name my $pkv; # primary key value my %vals; # hash of column -> value, including primary key + my $insert = sub { return unless %vals; my $sets = join(", ", map { "$_=" . $dbh->quote($vals{$_}) } keys %vals); - my $row = $dbh->selectrow_hashref(qq{ - SELECT * FROM $table WHERE $pk = ? - }, undef, $pkv); + my $row = $dbh->selectrow_hashref( + "SELECT * FROM $table WHERE $pk = ?", + undef, + $pkv, + ); if ( defined $row ) { # the row exists, let's update it $dbh->do("UPDATE $table SET $sets WHERE $pk=?", undef, $pkv); die $dbh->errstr if $dbh->err; - } else { + } + else { # find a propid for the row and then insert it my $propid_field = $propid_field{$table}; @@ -680,9 +750,12 @@ $propid++ while exists $existing_propids{$propid}; # finally, insert - $dbh->do(qq{ - INSERT INTO $table SET $propid_field = ?, $sets - }, undef, $propid); + $dbh->do( + "INSERT INTO $table SET $propid_field = ?, $sets", + undef, + $propid, + ); + die $dbh->errstr if $dbh->err; } @@ -693,7 +766,7 @@ while (<$fh>) { next if /^\#/; - if (/^(\w+)\.(\w+):/) { + if ( /^(\w+)\.(\w+):/) { $insert->(); ($table, $pkv) = ($1, $2); $pk = $pk{$table} or die "Don't know non-numeric primary key for table '$table'"; @@ -701,15 +774,18 @@ $vals{"scope"} = $scope unless exists $noscope{$table}; next; } + if (/^\s+(\w+)\s*:\s*(.+)/) { die "Unexpected line: $_ when not in a block" unless $table; $vals{$1} = $2; next; } + if (/\S/) { die "Unxpected line: $_"; } } + $insert->(); close($fh); } @@ -717,17 +793,20 @@ sub populate_moods { # moods my $moodfile = "$ENV{'LJHOME'}/bin/upgrading/moods.dat"; + if (open(M, $moodfile)) { print "Populating mood data.\n"; my %mood; # id -> [ mood, parent_id ] my $sth = $dbh->prepare("SELECT moodid, mood, parentmood FROM moods"); $sth->execute; + while (@_ = $sth->fetchrow_array) { $mood{$_[0]} = [ $_[1], $_[2] ]; } my %moodtheme; # name -> [ id, des ] $sth = $dbh->prepare("SELECT moodthemeid, name, des FROM moodthemes WHERE is_public='Y'"); $sth->execute; + while (@_ = $sth->fetchrow_array) { $moodtheme{$_[1]} = [ $_[0], $_[2] ]; } my $themeid; # current themeid (from existing db or just made) @@ -747,8 +826,10 @@ if (/^MOODTHEME\s+(.+?)\s*:\s*(.+)$/) { my ($name, $des) = ($1, $2); %data = (); + if ($moodtheme{$name}) { $themeid = $moodtheme{$name}->[0]; + if ($moodtheme{$name}->[1] ne $des) { $dbh->do("UPDATE moodthemes SET des=? WHERE moodthemeid=?", undef, $des, $themeid); @@ -756,10 +837,12 @@ $sth = $dbh->prepare("SELECT moodid, picurl, width, height ". "FROM moodthemedata WHERE moodthemeid=?"); $sth->execute($themeid); + while (@_ = $sth->fetchrow_array) { $data{$_[0]} = "$_[1]$_[2]$_[3]"; } - } else { + } + else { $dbh->do("INSERT INTO moodthemes (ownerid, name, des, is_public) ". "VALUES (?,?,?,'Y')", undef, $su->{'userid'}, $name, $des); $themeid = $dbh->{'mysql_insertid'}; @@ -786,6 +869,7 @@ foreach my $tbl (qw(schemacols schematables)) { my $sth = $dbh->prepare("SELECT DISTINCT tablename FROM $tbl"); $sth->execute; + while (my $doctbl = $sth->fetchrow_array) { next if $table_create{$doctbl}; $dbh->do("DELETE FROM $tbl WHERE tablename=?", undef, $doctbl); @@ -805,6 +889,7 @@ # verify domain exists? my $domain = $LJ::MOGILEFS_CONFIG{domain}; + unless (defined $exists->{$domain}) { print "\tCreating domain $domain...\n"; $mgd->create_domain($domain) @@ -821,7 +906,8 @@ $mgd->update_class($domain, $class, $LJ::MOGILEFS_CONFIG{classes}->{$class}) or die "Error: Unable to update class.\n"; } - } else { + } + else { # create it print "\tCreating class $class...\n"; $mgd->create_class($domain, $class, $LJ::MOGILEFS_CONFIG{classes}->{$class}) @@ -863,13 +949,11 @@ } } -sub skip_opt -{ +sub skip_opt { return $opt_skip; } -sub do_sql -{ +sub do_sql { my $sql = shift; chomp $sql; my $disp_sql = $sql; @@ -885,8 +969,7 @@ } } -sub try_sql -{ +sub try_sql { my $sql = shift; print "$sql;\n"; if ($opt_sql) { @@ -898,8 +981,7 @@ } } -sub try_alter -{ +sub try_alter { my ($table, $sql) = @_; return if $cluster && ! defined $clustered_table{$table}; @@ -909,11 +991,10 @@ clear_table_info($table); } -sub do_alter -{ +sub do_alter { my ($table, $sql) = @_; return if $cluster && ! defined $clustered_table{$table}; - + if ($LJ::IS_DEV_SERVER || $opt_force_production_alter) { do_sql($sql); # columns will have changed, so clear cache: @@ -925,8 +1006,7 @@ } } -sub create_table -{ +sub create_table { my $table = shift; return if $cluster && ! defined $clustered_table{$table}; @@ -956,8 +1036,7 @@ } } -sub drop_table -{ +sub drop_table { my $table = shift; return if $cluster && ! defined $clustered_table{$table}; @@ -968,15 +1047,13 @@ } } -sub mark_clustered -{ +sub mark_clustered { foreach (@_) { $clustered_table{$_} = 1; } } -sub register_tablecreate -{ +sub register_tablecreate { my ($table, $create) = @_; # we now know of it delete $table_unknown{$table}; @@ -986,28 +1063,25 @@ $table_create{$table} = $create; } -sub register_tabledrop -{ +sub register_tabledrop { my ($table) = @_; $table_drop{$table} = 1; } -sub post_create -{ +sub post_create { my $table = shift; + while (my ($type, $what) = splice(@_, 0, 2)) { push @{$post_create{$table}}, [ $type, $what ]; } } -sub register_alter -{ +sub register_alter { my $sub = shift; push @alters, $sub; } -sub clear_table_info -{ +sub clear_table_info { my $table = shift; delete $coltype{$table}; delete $indexname{$table}; @@ -1016,14 +1090,14 @@ sub table_exists { my $table = shift; - + ## TODO: use $dbh->table_info() to retrieve this info my $data_source = $dbh->get_info( $GetInfoType{SQL_DATA_SOURCE_NAME} ); if ($data_source =~ /^dbi:mysql:(\w+)/) { my $schema = $1; my $result = $dbh->selectrow_array(' - SELECT count(*) - FROM information_schema.tables + SELECT count(*) + FROM information_schema.tables WHERE table_schema = ? AND table_name = ? ', undef, $schema, $table); @@ -1031,8 +1105,7 @@ } } -sub load_table_info -{ +sub load_table_info { my $table = shift; clear_table_info($table); Modified: trunk/cgi-bin/LJ/Entry.pm =================================================================== --- trunk/cgi-bin/LJ/Entry.pm 2011-09-16 07:46:45 UTC (rev 20058) +++ trunk/cgi-bin/LJ/Entry.pm 2011-09-16 08:26:11 UTC (rev 20059) @@ -351,14 +351,14 @@ # @entries - array of LJ::Entry objects # # See also instance method '_load_props' -# +# sub preload_props { my ($class, $entlist) = @_; - - ## %needed_to_load: userid --> [LJ::Entry, LJ::Entry, ... ] + + ## %needed_to_load: userid --> [LJ::Entry, LJ::Entry, ... ] ## %users: userid --> LJ::User - my (%needed_to_load, %users); - + my (%needed_to_load, %users); + foreach my $en (@$entlist) { next if $en->{_loaded_props}; my $u = $en->{u}; @@ -850,7 +850,7 @@ $opts->{journalid} = $self->journalid; $opts->{posterid} = $self->posterid; $opts->{entry_url} = $self->prop('reposted_from') || $self->url; - + $self->_load_text unless $self->{_loaded_text}; my $event = $self->{event}; LJ::CleanHTML::clean_event(\$event, $opts); @@ -930,7 +930,7 @@ my $poster = $self->poster; return 0 if $poster->{statusvis} eq 'S'; - # if poster choosed to delete jouranl and all external content, + # if poster choosed to delete jouranl and all external content, # then don't show his/her entries, except in some protected journals like 'lj_core' if ($poster->{statusvis} eq 'D') { my ($purge_comments, $purge_community_entries) = split /:/, $poster->prop("purge_external_content"); @@ -1398,7 +1398,7 @@ ## returns 'yes' if entry is ads-eligible (there are no offensive terms etc), and 'no' otherwise sub check_for_negative_terms { my $self = shift; - + my $tags = $self->prop('personifi_tags'); return $1 if $tags =~ /nterms:(\w+)/; my $nterms = ( ($self->subject_raw . ' '. $self->event_raw) =~ /($LJ::NEGATIVE_TERMS)/) ?'no':'yes'; @@ -1480,20 +1480,25 @@ my ($idsbyc, $type, $ret) = @_; my $opts = {}; + if ($type eq 'text') { $opts->{text_only} = 1; - } elsif ($type eq 'prop') { + } + elsif ($type eq 'prop') { $opts->{prop_only} = 1; - } else { + } + else { return undef; } my @postids; + while (my ($cid, $ids) = each %$idsbyc) { foreach my $pair (@$ids) { push @postids, [ $cid, $pair->[0], $pair->[1] ]; } } + my $rawposts = LJ::get_posts_raw($opts, @postids); # add replycounts fields to props @@ -1505,10 +1510,12 @@ # translate colon-separated (new) to space-separated (old) keys. $ret ||= {}; + while (my ($id, $data) = each %{$rawposts->{$type}}) { $id =~ s/:/ /; $ret->{$id} = $data; } + return $ret; } @@ -1646,17 +1653,21 @@ my $fetchprop = sub { my $db = shift; return unless %$cneedprop; + my $in = $make_in->(keys %$cneedprop); $sth = $db->prepare("SELECT journalid, jitemid, propid, value ". "FROM logprop2 WHERE $in"); $sth->execute; my %gotid; + while (my ($jid, $jitemid, $propid, $value) = $sth->fetchrow_array) { my $id = "$jid:$jitemid"; - my $propname = $LJ::CACHE_PROPID{'log'}->{$propid}{name}; + LJ::load_props('log') unless defined LJ::MemCache::get('CACHE_PROPID'); + my $propname = LJ::MemCache::get('CACHE_PROPID')->{'log'}->{$propid}{name}; $ret->{prop}{$id}{$propname} = $value; $gotid{$id} = 1; } + foreach my $id (keys %gotid) { my ($jid, $jitemid) = map { $_ + 0 } split(/:/, $id); LJ::MemCache::add([$jid, "logprop:$id"], $ret->{prop}{$id}); @@ -2218,10 +2229,12 @@ } } + # move reply count to props hash foreach ( keys %rc ) { $hashref->{$_}{'replycount'} = $rc{$_}; } + # return if all props loaded from memcached return unless %needprops || %needrc; unless ( $db ) { @@ -2230,6 +2243,7 @@ return unless $db; } + # if not all props loaded if ( %needprops ) { LJ::load_props("log"); my $in = join(",", keys %needprops); @@ -2237,19 +2251,23 @@ "WHERE journalid=? AND jitemid IN ($in)"); $sth->execute($userid); + my $propid = LJ::MemCache::get('CACHE_PROPID')->{'log'}; + while (my ($jitemid, $propid, $value) = $sth->fetchrow_array) { - $hashref->{$jitemid}->{$LJ::CACHE_PROPID{'log'}->{$propid}->{'name'}} = $value; + $hashref->{$jitemid}->{ $propid->{$propid}->{'name'} } = $value; } foreach my $id (keys %needprops) { - LJ::MemCache::set([$userid,"logprop:$userid:$id"], $hashref->{$id} || {}); - } + LJ::MemCache::set([$userid, "logprop:$userid:$id"], $hashref->{$id} || {}); + } } + # if not all reply counts loaded if (%needrc) { my $in = join(",", keys %needrc); my $sth = $db->prepare("SELECT jitemid, replycount FROM log2 WHERE journalid=? AND jitemid IN ($in)"); $sth->execute($userid); + while (my ($jitemid, $rc) = $sth->fetchrow_array) { $hashref->{$jitemid}->{'replycount'} = $rc; LJ::MemCache::add(LJ::Entry::reply_count_memkey($userid, $jitemid), $rc); @@ -2420,9 +2438,9 @@ my ($u, $jitemid, $action, $value) = @_; # check action name - die "unknown action: $action" + die "unknown action: $action" unless $action =~ /^(init)|(incr)|(decr)$/; - + $value = 1 unless defined $value; my $uid = $u->{'userid'}; my $memkey = LJ::Entry::reply_count_memkey($uid, $jitemid); @@ -2434,19 +2452,19 @@ } return 0 unless $u->writer; - + ## my $update_memc = $action eq 'decr' ? sub { LJ::MemCache::decr($memkey, $value) } : sub { LJ::MemCache::incr($memkey, $value) }; - + my $entry = LJ::Entry->new( $u, 'jitemid' => $jitemid ); LJ::run_hooks( 'replycount_change', $entry ); ## my $sql_sign = $action eq 'decr' ? '-' : '+'; my $sql = "UPDATE log2 SET replycount=LAST_INSERT_ID(replycount $sql_sign $value) WHERE journalid=? AND jitemid=?"; - + unless ( LJ::MemCache::can_gets() ){ # used Cache::Memcached driver that does not support 'gets' and 'cas' commands $u->selectrow_array("SELECT GET_LOCK(?,10)", undef, $memkey); @@ -2461,7 +2479,7 @@ } $u->selectrow_array("SELECT RELEASE_LOCK(?)", undef, $memkey); - + } else { # used Cache::Memcached::Fast @@ -2469,7 +2487,7 @@ LJ::run_hooks('report_entry_update', $uid, $jitemid); $u->do($sql, undef, $uid, $jitemid); if (@LJ::MEMCACHE_SERVERS and not defined $ret) { - ## Lock free update + ## Lock free update my $max_loops = 100; # prevent infinite loop while ($max_loops--){ my $gets = LJ::MemCache::gets($memkey); # get [cas, $val] @@ -2495,24 +2513,26 @@ my ($u, $jitemid, $action, $value) = @_; # check action name - die "unknown action: $action" + die "unknown action: $action" unless $action =~ /^(init)|(incr)|(decr)$/; - + return 0 unless $value; - + my $entry = LJ::Entry->new( $u, 'jitemid' => $jitemid ); my $spam_counter = $entry->prop('spam_counter') || 0; - + if ($action eq 'init') { $entry->set_prop('spam_counter', 0); return 1; - } elsif ($action eq 'decr') { + } + elsif ($action eq 'decr') { return 0 if $spam_counter - $value < 0; $entry->set_prop('spam_counter', $spam_counter - $value); - } elsif ($action eq 'incr') { + } + elsif ($action eq 'incr') { $entry->set_prop('spam_counter', $spam_counter + $value); } - + return 1; } Modified: trunk/cgi-bin/LJ/User/PropStorage/DB.pm =================================================================== --- trunk/cgi-bin/LJ/User/PropStorage/DB.pm 2011-09-16 07:46:45 UTC (rev 20058) +++ trunk/cgi-bin/LJ/User/PropStorage/DB.pm 2011-09-16 08:26:11 UTC (rev 20059) @@ -15,6 +15,7 @@ LJ::load_props('user'); my @propids; + foreach my $k (@$props) { my $propinfo = LJ::get_prop('user', $k); my $propid = $propinfo->{'id'}; @@ -22,17 +23,20 @@ push @propids, $propid; } - my %propid_map = %{ $LJ::CACHE_PROPID{'user'} }; + LJ::load_props('user') unless defined LJ::MemCache::get('CACHE_PROPID'); + my %propid_map = %{ LJ::MemCache::get('CACHE_PROPID')->{'user'} }; my %ret = map { $_ => undef } @$props; my $sql; + if ( my $propids_in = join(',', @propids) ) { $sql = qq{ SELECT upropid, value FROM $table WHERE userid=? AND upropid IN ($propids_in) }; - } else { + } + else { # well, it seems that they didn't pass us any props, so # let's use a somewhat different SQL query to load everything # from the given table Modified: trunk/cgi-bin/LJ/User/PropStorage.pm =================================================================== --- trunk/cgi-bin/LJ/User/PropStorage.pm 2011-09-16 07:46:45 UTC (rev 20058) +++ trunk/cgi-bin/LJ/User/PropStorage.pm 2011-09-16 08:26:11 UTC (rev 20059) @@ -135,8 +135,10 @@ my ( $class, $packed ) = @_; my %ret; + foreach my $propid (keys %$packed) { - my $propname = $LJ::CACHE_PROPID{'user'}->{$propid}->{'name'}; + LJ::load_props('user') unless defined LJ::MemCache::get('CACHE_PROPID'); + my $propname = LJ::MemCache::get('CACHE_PROPID')->{'user'}->{$propid}->{'name'}; next unless defined $propname; $ret{$propname} = $packed->{$propid}; } Modified: trunk/cgi-bin/ljlib.pl =================================================================== --- trunk/cgi-bin/ljlib.pl 2011-09-16 07:46:45 UTC (rev 20058) +++ trunk/cgi-bin/ljlib.pl 2011-09-16 08:26:11 UTC (rev 20059) @@ -1654,30 +1654,38 @@ # des-table: a list of tables' proplists to load. Can be one of # "log", "talk", "user", or "rate". # </LJFUNC> -sub load_props -{ +sub load_props { my $dbarg = ref $_[0] ? shift : undef; my @tables = @_; my $dbr; - my %keyname = qw(log propid - talk tpropid - user upropid - rate rlid - ); + my %keyname = ( + 'log' => 'propid', + 'talk' => 'tpropid', + 'user' => 'upropid', + 'rate' => 'rlid', + ); + my $prop = LJ::MemCache::get('CACHE_PROP'); + my $propid = LJ::MemCache::get('CACHE_PROPID'); + foreach my $t (@tables) { next unless defined $keyname{$t}; - next if defined $LJ::CACHE_PROP{$t}; + next if defined $prop->{$t} && defined $propid->{$t}; + my $tablename = $t eq "rate" ? "ratelist" : "${t}proplist"; $dbr ||= LJ::get_db_reader(); my $sth = $dbr->prepare("SELECT * FROM $tablename"); $sth->execute; + while (my $p = $sth->fetchrow_hashref) { $p->{'id'} = $p->{$keyname{$t}}; - $LJ::CACHE_PROP{$t}->{$p->{'name'}} = $p; - $LJ::CACHE_PROPID{$t}->{$p->{'id'}} = $p; + $prop->{$t}->{$p->{'name'}} = $p; + $propid->{$t}->{$p->{'id'}} = $p; } } + + LJ::MemCache::set('CACHE_PROP', $prop); + LJ::MemCache::set('CACHE_PROPID', $propid); } # <LJFUNC> @@ -1693,26 +1701,27 @@ # "log", "talk", or "user". # des-name: the name of the prop to get the hashref of. # </LJFUNC> -sub get_prop -{ +sub get_prop { my $table = shift; my $name = shift; - unless (defined $LJ::CACHE_PROP{$table} && $LJ::CACHE_PROP{$table}->{$name}) { - $LJ::CACHE_PROP{$table} = undef; + + my $prop = LJ::MemCache::get('CACHE_PROP'); + + unless (defined $prop->{$table} && $prop->{$table}->{$name}) { LJ::load_props($table); } - unless ($LJ::CACHE_PROP{$table}) { + unless ($prop->{$table}) { warn "Prop table does not exist: $table" if $LJ::IS_DEV_SERVER; return undef; } - unless ($LJ::CACHE_PROP{$table}->{$name}) { - warn "Prop does not exist: $table - $name" if $LJ::IS_DEV_SERVER; + unless ($prop->{$table}->{$name}) { + warn "Prop does not exist: $table - $name" if $LJ::IS_DEV_SERVER && $name ne 'replycount'; return undef; } - return $LJ::CACHE_PROP{$table}->{$name}; + return LJ::MemCache::get('CACHE_PROP')->{$table}->{$name}; } # <LJFUNC> @@ -1725,36 +1734,35 @@ # and their associated values being hashrefs to where you # want that data to be populated. # </LJFUNC> -sub load_codes -{ +sub load_codes { &nodb; my $req = shift; my $dbr = LJ::get_db_reader() or die "Unable to get database handle"; - foreach my $type (keys %{$req}) - { + foreach my $type (keys %{$req}) { my $memkey = "load_codes:$type"; - unless ($LJ::CACHE_CODES{$type} ||= LJ::MemCache::get($memkey)) - { + + unless ($LJ::CACHE_CODES{$type} ||= LJ::MemCache::get($memkey)) { $LJ::CACHE_CODES{$type} = []; my $sth = $dbr->prepare("SELECT code, item, sortorder FROM codes WHERE type=?"); $sth->execute($type); - while (my ($code, $item, $sortorder) = $sth->fetchrow_array) - { + + while (my ($code, $item, $sortorder) = $sth->fetchrow_array) { push @{$LJ::CACHE_CODES{$type}}, [ $code, $item, $sortorder ]; } + @{$LJ::CACHE_CODES{$type}} = sort { $a->[2] <=> $b->[2] } @{$LJ::CACHE_CODES{$type}}; LJ::MemCache::set($memkey, $LJ::CACHE_CODES{$type}, 60*15); } - foreach my $it (@{$LJ::CACHE_CODES{$type}}) - { + foreach my $it (@{$LJ::CACHE_CODES{$type}}) { if (ref $req->{$type} eq "HASH") { $req->{$type}->{$it->[0]} = $it->[1]; - } elsif (ref $req->{$type} eq "ARRAY") { + } + elsif (ref $req->{$type} eq "ARRAY") { push @{$req->{$type}}, { 'code' => $it->[0], 'item' => $it->[1] }; } } @@ -2211,7 +2219,6 @@ $LJ::DBIRole->flush_cache(); - %LJ::CACHE_PROP = (); %LJ::CACHE_STYLE = (); $LJ::CACHED_MOODS = 0; $LJ::CACHED_MOOD_MAX = 0; @@ -2777,8 +2784,7 @@ # des-: # returns: # </LJFUNC> -sub load_talk_props2 -{ +sub load_talk_props2 { my $db = isdb($_[0]) ? shift @_ : undef; my ($uuserid, $listref, $hashref) = @_; @@ -2789,11 +2795,13 @@ my %need; my @memkeys; + foreach (@$listref) { - my $id = $_+0; + my $id = $_ + 0; $need{$id} = 1; push @memkeys, [$userid,"talkprop:$userid:$id"]; } + return $hashref unless %need; my $mem = LJ::MemCache::get_multi(@memkeys) || {}; @@ -2812,7 +2820,10 @@ if (!$db || @LJ::MEMCACHE_SERVERS) { $u ||= LJ::load_userid($userid); - $db = @LJ::MEMCACHE_SERVERS ? LJ::get_cluster_def_reader($u) : LJ::get_cluster_reader($u); + $db = @LJ::MEMCACHE_SERVERS + ? LJ::get_cluster_def_reader($u) + : LJ::get_cluster_reader($u); + return $hashref unless $db; } @@ -2821,14 +2832,18 @@ my $sth = $db->prepare("SELECT jtalkid, tpropid, value FROM talkprop2 ". "WHERE journalid=? AND jtalkid IN ($in)"); $sth->execute($userid); + while (my ($jtalkid, $propid, $value) = $sth->fetchrow_array) { - my $p = $LJ::CACHE_PROPID{'talk'}->{$propid}; + LJ::load_props('talk') unless defined LJ::MemCache::get('CACHE_PROPID'); + my $p = LJ::MemCache::get('CACHE_PROPID')->{'talk'}->{$propid}; next unless $p; $hashref->{$jtalkid}->{$p->{'name'}} = $value; } + foreach my $id (keys %need) { LJ::MemCache::set([$userid,"talkprop:$userid:$id"], $hashref->{$id} || {}); } + return $hashref; }