+ };
+}
+
+# called via PublicInbox::DS::event_loop
+sub event_step {
+ my ($ctx, $sync) = @_;
+ # can't find a partial match in current inbox, try the others:
+ my $ibx = shift @{$ctx->{again}} or return finalize_partial($ctx);
+ my $mids = search_partial($ibx, $ctx->{mid}) or
+ return ($sync ? undef : PublicInbox::DS::requeue($ctx));
+ $ctx->{n_partial} += scalar(@$mids);
+ push @{$ctx->{partial}}, [ $ibx, $mids ];
+ $ctx->{n_partial} >= PARTIAL_MAX ? finalize_partial($ctx)
+ : ($sync ? undef : PublicInbox::DS::requeue($ctx));
+}
+
+sub finalize_exact {
+ my ($ctx) = @_;
+
+ return $ctx->{-wcb}->(exact($ctx)) if $ctx->{found};
+
+ # fall back to partial MID matching
+ my $mid = $ctx->{mid};
+ my $cur = $ctx->{ibx};
+ my $mids = search_partial($cur, $mid);
+ if ($mids) {
+ $ctx->{n_partial} = scalar(@$mids);
+ push @{$ctx->{partial}}, [ $cur, $mids ];
+ } elsif ($ctx->{again} && length($mid) >= $MIN_PARTIAL_LEN) {
+ bless $ctx, __PACKAGE__;
+ if ($ctx->{env}->{'pi-httpd.async'}) {
+ $ctx->event_step;
+ return;