couch_maint.pl Revision 316663353936 (Fri Mar 25 2011 at 10:45) - Diff Link to this snippet: https://friendpaste.com/4lSkgZR0xdAJ2Od42U0Yiz Embed: manni perldoc borland colorful default murphy trac fruity autumn bw emacs pastie friendly Show line numbers Wrap lines 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596#!/usr/bin/perl# Takes one argument which is the URL of the JSON document to use# The JSON document describes the various# replications to ensure are running and what indexes need rebuilding and# how often that should be doneuse JSON::Any;use LWP::UserAgent;use Storable;use CouchDB::Client;use Time::ParseDate qw(parsedate); # from libtime-modules-perlmy ($url) = @ARGV;die "First and only argument must be a URL" unless $url;# Get our persistent state (or a blank one)my $state = {};eval { # We can keep it in the temp folder because it doesn't matter much if we loose it, we just count from scratch $state = retrieve("/tmp/couch_maint.state");};# Since we've been passed a URL use LWP to grab it (rather than CouchDB::Client)my $ua = LWP::UserAgent->new();my $response = $ua->get($url);die "Failed to download document" unless $response->is_success();my $info = JSON::Any->jsonToObj($response->content);die "Document doesn't contain a 'url' key so don't know how to contact the couch service\n" unless $info->{uri};my $couch_client = CouchDB::Client->new( uri => $info->{uri} );$couch_client->testConnection or die "Can't talk to the couch database on $info->{uri}";die "No databases field in the document" unless $info->{databases};# For each databasewhile(my ($database, $db_info) = each %{$info->{databases}}) { my $db = $couch_client->newDB($database); # Trigger the indexes if ($db_info->{index}) { while( my ($index, $index_info) = each %{$db_info->{index}}) { if (run_now($database,$index)) { my $design_doc = $db->newDesignDoc("_design/$index"); $design_doc->retrieve(); my @views = $design_doc->listViews(); # Eval the view query because it might timeout if the view is a long way behind. # This is ok, at least we triggered it and by evaling we still get to trigger the # other views (admittedly significantly slower than we might like but better than nothing) # The only real danger is that each time we trigger but timeout we eat a handle off the # couch server which will only be returned when the view eventually finishes building and # hence we can run it out of handles. Not a lot we can do about that here though really. eval { # In theory triggering the first view # will update the whole lot but lets # just trigger all of them to be safe for(@views) { $design_doc->queryView($_, limit => 0); } }; warn $@ if $@; calc_next($index_info,$database,$index); } } } # Set the replication, we only do push replication (you can do this over and over again and couch is ok with that) if ($db_info->{replicate}) { foreach my $target (@{$db_info->{replicate}}) { $db->replicate(target => $target, continuous => 1); } }}store($state,"/tmp/couch_maint.state");sub run_now { my ($a,$b) = @_; return 1 unless defined($state->{$a}->{$b}); # If we know nothing we want to run it now return time()>$state->{$a}->{$b}->{next_run};}sub calc_next { my ($info,$a,$b) = @_; my ($next_time,$error) = parsedate($info->{every}); if ($next_time){ # We subtract 10 seconds from the next run time so that # under normal running (when we can index very quickly) # doing it every minute from cron might actually cause # it to happen every minute. Without this if we are # invoked once a minute there is a chance we will decide # not to index because it was only 59 seconds since we last # did it and therefore we won't reindex until almost # 2 minutes have passed. $state->{$a}->{$b}->{next_run} = $next_time-10; } else { die "Error parsing time reference '$info->{every}': $error\n"; }}