perlsky is a Perl 5 implementation of an AT Protocol Personal Data Server.
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

Cache local appview post index across requests

alice 0948d93d 4c8ca6e5

+86 -2
+1
lib/ATProto/PDS/ServiceProxy.pm
··· 57 57 ); 58 58 59 59 has settings => sub { {} }; 60 + has local_post_index_cache => sub { undef }; 60 61 has ua => sub { 61 62 my $ua = Mojo::UserAgent->new(max_redirects => 0); 62 63 $ua->request_timeout(15);
+20 -2
lib/ATProto/PDS/ServiceProxy/Threads.pm
··· 207 207 my $index = $c->stash('local_post_index'); 208 208 return $index if $index; 209 209 210 - $index = { 210 + my $event_seq = $c->store->latest_event_seq; 211 + my $cache = $self->local_post_index_cache; 212 + if ($cache && (($cache->{event_seq} // -1) == $event_seq)) { 213 + $c->stash(local_post_index => $cache->{index}); 214 + return $cache->{index}; 215 + } 216 + 217 + $index = _build_local_post_index($self, $c); 218 + $self->local_post_index_cache({ 219 + event_seq => $event_seq, 220 + index => $index, 221 + }); 222 + $c->stash(local_post_index => $index); 223 + return $index; 224 + } 225 + 226 + # Local appview reads can hit this repeatedly across requests, so keep the 227 + # expensive scan isolated behind an event-seq keyed cache. 228 + sub _build_local_post_index ($self, $c) { 229 + my $index = { 211 230 replies => {}, 212 231 stats => {}, 213 232 viewer => {}, ··· 258 277 $index->{replies}{$parent_uri} = \@sorted; 259 278 } 260 279 261 - $c->stash(local_post_index => $index); 262 280 return $index; 263 281 } 264 282
+65
t/service-proxy-local.t
··· 38 38 39 39 sub list_accounts { 40 40 my ($self) = @_; 41 + $self->{list_accounts_calls}++; 41 42 return $self->{list_accounts} // []; 43 + } 44 + 45 + sub all_records_for_did { 46 + my ($self, $did) = @_; 47 + $self->{all_records_for_did_calls}{$did}++; 48 + return $self->{all_records_for_did}{$did} // []; 49 + } 50 + 51 + sub latest_event_seq { 52 + my ($self) = @_; 53 + $self->{latest_event_seq_calls}++; 54 + return $self->{latest_event_seq} // 0; 42 55 } 43 56 44 57 sub get_record { ··· 64 77 my ($self, $name, $default) = @_; 65 78 return $default; 66 79 } 80 + 81 + sub stash { 82 + my ($self, @args) = @_; 83 + $self->{stash} //= {}; 84 + return $self->{stash}{$args[0]} if @args == 1; 85 + if (@args == 2) { 86 + $self->{stash}{$args[0]} = $args[1]; 87 + return $self; 88 + } 89 + die 'unsupported stash arity'; 90 + } 67 91 } 68 92 69 93 my $proxy = ATProto::PDS::ServiceProxy->new; ··· 119 143 undef, 120 144 'remote posts still fall back to upstream handling', 121 145 ); 146 + 147 + my $cache_store = LocalTestStore->new( 148 + latest_event_seq => 1, 149 + list_accounts => [ 150 + { 151 + did => $did, 152 + handle => 'alice.test', 153 + }, 154 + ], 155 + all_records_for_did => { 156 + $did => [ 157 + { 158 + collection => 'app.bsky.feed.post', 159 + rkey => 'cached-post', 160 + cid => 'bafyreicached', 161 + value => { text => 'cached' }, 162 + }, 163 + ], 164 + }, 165 + ); 166 + 167 + my $cached_proxy = ATProto::PDS::ServiceProxy->new; 168 + my $first_cache_context = LocalTestContext->new($cache_store); 169 + my $second_cache_context = LocalTestContext->new($cache_store); 170 + my $third_cache_context = LocalTestContext->new($cache_store); 171 + 172 + my $first_index = $cached_proxy->_local_post_index($first_cache_context); 173 + is($cache_store->{list_accounts_calls}, 1, 'first local post index build scans accounts once'); 174 + is($cache_store->{all_records_for_did_calls}{$did}, 1, 'first local post index build scans records once'); 175 + 176 + my $second_index = $cached_proxy->_local_post_index($second_cache_context); 177 + is($cache_store->{latest_event_seq_calls}, 2, 'subsequent requests still check the latest event seq'); 178 + is($cache_store->{list_accounts_calls}, 1, 'unchanged event seq reuses the cached global post index'); 179 + is($cache_store->{all_records_for_did_calls}{$did}, 1, 'unchanged event seq avoids rescanning records'); 180 + is($second_index, $first_index, 'unchanged event seq returns the cached index reference'); 181 + 182 + $cache_store->{latest_event_seq} = 2; 183 + my $third_index = $cached_proxy->_local_post_index($third_cache_context); 184 + is($cache_store->{list_accounts_calls}, 2, 'new events invalidate the cached global post index'); 185 + is($cache_store->{all_records_for_did_calls}{$did}, 2, 'new events trigger a rebuild scan'); 186 + isnt($third_index, $first_index, 'new events rebuild the cached index'); 122 187 123 188 done_testing;