読者です 読者をやめる 読者になる 読者になる

Subscription::LivedoorReader でコケると悲しい

出社して、さて前夜から溜った feed を読もうかな、と Plagger を走らせて。
未読 entry が 500 ほど溜った状態で

Can't use string ("500 Server closed connection wit") as a HASH ref while "strict refs" in
 use at /home/fujiwara/devel/plagger/lib/Plagger/Plugin/Subscription/LivedoorReader.pm line 80.

コケた。LDR のサーバがエラーを返したのに、それをデータとして処理しようとしたため。(リファレンスでないのをデリファレンスしようとした)

feed や entry を読み込んでメモリ上に乗っかった状態で (しかも LDR 側は既読にしちゃった後)、Store も Publish もする前に落ちると、未読が全部メモリの露と消える。悲しすぎる。

DB みたいにトランザクション処理、というのも難しいが、せめて少しでも被害を防ぐ patch.
diff の都合でやたらと長いけど、要は

my $feed = Plagger::Feed->new;

から

$context->update->add($feed);

までを eval で括って例外を捕捉。既読にする処理 ( touch_all ) は、そこまでの処理が正常に完了してから行うようにした。

Index: Subscription/LivedoorReader.pm
===================================================================
--- Subscription/LivedoorReader.pm	(revision 1018)
+++ Subscription/LivedoorReader.pm	(working copy)
@@ -72,39 +72,43 @@
     for my $sub (@$subs) {
         $context->log(debug => "get unread items of $sub->{subscribe_id}");
         my $data = $self->_request("/api/unread", { subscribe_id => $sub->{subscribe_id} });
-        $self->_request("/api/touch_all", { subscribe_id => $sub->{subscribe_id} })
-            if $mark_read;
+        eval {
+            my $feed = Plagger::Feed->new;
+            $feed->type('livedoorReader');
+            $feed->title( Plagger::Util::strip_html($data->{channel}->{title}) );
+            $feed->link($data->{channel}->{link});
+            $feed->url($data->{channel}->{feedlink});
+            $feed->image({ url => $data->{channel}->{image} || $sub->{icon} });
+            $feed->meta->{livedoor_reader_id} = $sub->{subscribe_id};
+            $feed->meta->{rate} = $sub->{rate};
+            $feed->add_tag($_) for @{$sub->{tags}};
+            $feed->add_tag($sub->{folder}) if $sub->{folder};
+            $feed->updated( Plagger::Date->from_epoch($sub->{modified_on}) ) if $sub->{modified_on};
+            $feed->description($data->{channel}->{description});
+            $feed->meta->{livedoor_reader_subscribers_count} = $data->{channel}->{subscribers_count};
 
-        my $feed = Plagger::Feed->new;
-        $feed->type('livedoorReader');
-        $feed->title( Plagger::Util::strip_html($data->{channel}->{title}) );
-        $feed->link($data->{channel}->{link});
-        $feed->url($data->{channel}->{feedlink});
-        $feed->image({ url => $data->{channel}->{image} || $sub->{icon} });
-        $feed->meta->{livedoor_reader_id} = $sub->{subscribe_id};
-        $feed->meta->{rate} = $sub->{rate};
-        $feed->add_tag($_) for @{$sub->{tags}};
-        $feed->add_tag($sub->{folder}) if $sub->{folder};
-        $feed->updated( Plagger::Date->from_epoch($sub->{modified_on}) ) if $sub->{modified_on};
-        $feed->description($data->{channel}->{description});
-        $feed->meta->{livedoor_reader_subscribers_count} = $data->{channel}->{subscribers_count};
+            for my $item ( @{$data->{items}} ) {
+                my $entry = Plagger::Entry->new;
+                $entry->title($item->{title});
+                $entry->author($item->{author}) if $item->{author};
+                $entry->link($item->{link});
+                # TODO support enclosure
+                $entry->tags([ $item->{category} ]) if $item->{category};
+                $entry->date( Plagger::Date->from_epoch($item->{modified_on}) ); # xxx created_on as well
+                $entry->meta->{livedoor_reader_item_id} = $item->{id};
+                $entry->feed_link($feed->link);
+                $entry->body($item->{body});
 
-        for my $item ( @{$data->{items}} ) {
-            my $entry = Plagger::Entry->new;
-            $entry->title($item->{title});
-            $entry->author($item->{author}) if $item->{author};
-            $entry->link($item->{link});
-            # TODO support enclosure
-            $entry->tags([ $item->{category} ]) if $item->{category};
-            $entry->date( Plagger::Date->from_epoch($item->{modified_on}) ); # xxx created_on as well
-            $entry->meta->{livedoor_reader_item_id} = $item->{id};
-            $entry->feed_link($feed->link);
-            $entry->body($item->{body});
-
-            $feed->add_entry($entry);
+                $feed->add_entry($entry);
+            }
+            $context->update->add($feed);
+        };
+        if($@){
+            $context->log(error => $@);
+            next;
         }
-
-        $context->update->add($feed);
+        $self->_request("/api/touch_all", { subscribe_id => $sub->{subscribe_id} })
+            if $mark_read;
     }
 }