出社して、さて前夜から溜った 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; } }