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.

Reuse local appview caches for post resolution

alice 2a0c9047 635a4cc9

+58 -2
+19 -1
lib/ATProto/PDS/ServiceProxy/Posts.pm
··· 20 20 ); 21 21 22 22 sub _resolve_local_post_uri ($self, $c, $uri) { 23 + my $cache = $c->stash('service_proxy_local_post_uri_cache') || {}; 24 + if (exists $cache->{$uri}) { 25 + return $cache->{$uri}; 26 + } 27 + 23 28 my ($repo, $collection, $rkey) = parse_at_uri($uri); 24 29 return undef unless defined $repo && defined $collection && defined $rkey; 25 30 my $account = resolve_repo($c, $repo) or return undef; 26 31 xrpc_error(404, 'RecordNotFound', 'Record was not found') 27 32 unless $collection eq 'app.bsky.feed.post'; 33 + my $canonical_uri = 'at://' . $account->{did} . '/' . $collection . '/' . $rkey; 34 + my $local_post_index = $c->stash('local_post_index'); 35 + if ($local_post_index && $local_post_index->{posts}{$canonical_uri}) { 36 + my $resolved = $local_post_index->{posts}{$canonical_uri}; 37 + $cache->{$uri} = $resolved; 38 + $cache->{$canonical_uri} = $resolved; 39 + $c->stash(service_proxy_local_post_uri_cache => $cache); 40 + return $resolved; 41 + } 28 42 my $row = $c->store->get_record($account->{did}, $collection, $rkey); 29 43 xrpc_error(404, 'RecordNotFound', 'Record was not found') unless $row; 30 - return [ $account, $row ]; 44 + my $resolved = [ $account, $row ]; 45 + $cache->{$uri} = $resolved; 46 + $cache->{$canonical_uri} = $resolved; 47 + $c->stash(service_proxy_local_post_uri_cache => $cache); 48 + return $resolved; 31 49 } 32 50 33 51 sub _post_uri ($self, $account, $row) {
+7 -1
lib/ATProto/PDS/ServiceProxy/Profile.pm
··· 50 50 } 51 51 52 52 sub _profile_record_value ($self, $c, $account) { 53 + my $cache = $c->stash('service_proxy_profile_record_value_cache') || {}; 54 + return $cache->{ $account->{did} } if exists $cache->{ $account->{did} }; 55 + 53 56 my $profile = $c->store->get_record($account->{did}, 'app.bsky.actor.profile', 'self'); 54 - return (ref($profile) eq 'HASH' && ref($profile->{value}) eq 'HASH') ? $profile->{value} : {}; 57 + my $value = (ref($profile) eq 'HASH' && ref($profile->{value}) eq 'HASH') ? $profile->{value} : {}; 58 + $cache->{ $account->{did} } = $value; 59 + $c->stash(service_proxy_profile_record_value_cache => $cache); 60 + return $value; 55 61 } 56 62 57 63 sub _profile_view_basic ($self, $c, $account, $profile_value = undef) {
+2
lib/ATProto/PDS/ServiceProxy/Threads.pm
··· 237 237 @{ $c->store->get_accounts_by_dids([ sort keys %did_seen ]) }; 238 238 my $index = { 239 239 replies => {}, 240 + posts => {}, 240 241 stats => {}, 241 242 viewer => {}, 242 243 }; ··· 247 248 next unless ref($value) eq 'HASH'; 248 249 249 250 if (($row->{collection} // q()) eq 'app.bsky.feed.post') { 251 + $index->{posts}{ $self->_post_uri($account, $row) } = [ $account, $row ]; 250 252 my $reply = $value->{reply}; 251 253 if (ref($reply) eq 'HASH') { 252 254 my $parent_uri = $reply->{parent}{uri} // q();
+30
t/service-proxy-local.t
··· 28 28 29 29 sub get_account_by_handle { 30 30 my ($self, $handle) = @_; 31 + $self->{get_account_by_handle_calls}{$handle}++; 31 32 return $self->{accounts_by_handle}{$handle}; 32 33 } 33 34 34 35 sub get_account_by_did { 35 36 my ($self, $did) = @_; 37 + $self->{get_account_by_did_calls}{$did}++; 36 38 return $self->{accounts_by_did}{$did}; 37 39 } 38 40 ··· 77 79 78 80 sub get_record { 79 81 my ($self, $did, $collection, $rkey) = @_; 82 + $self->{get_record_calls}{"$did|$collection|$rkey"}++; 80 83 return $self->{records}{"$did|$collection|$rkey"}; 81 84 } 82 85 } ··· 146 149 my $resolved_by_handle = $proxy->_resolve_local_post_uri($c, 'at://alice.test/app.bsky.feed.post/present-post'); 147 150 is($resolved_by_handle->[0]{did}, $did, 'handle-form local post lookup returns the local account'); 148 151 is($resolved_by_handle->[1]{rkey}, 'present-post', 'handle-form local post lookup returns the local record'); 152 + 153 + my $cached_resolved_by_did = $proxy->_resolve_local_post_uri($c, "at://$did/app.bsky.feed.post/present-post"); 154 + is($cached_resolved_by_did->[0]{did}, $did, 'repeat local post lookup reuses the cached account resolution'); 155 + is($cached_resolved_by_did->[1]{rkey}, 'present-post', 'repeat local post lookup reuses the cached record resolution'); 156 + is($store->{get_account_by_did_calls}{$did}, 1, 'repeat did-form local post lookup avoids another account lookup'); 157 + is($store->{get_record_calls}{"$did|app.bsky.feed.post|present-post"}, 2, 'repeat did-form local post lookup avoids another record fetch'); 158 + 159 + my $index_context = LocalTestContext->new($store); 160 + $index_context->stash(local_post_index => { 161 + posts => { 162 + "at://$did/app.bsky.feed.post/present-post" => $resolved, 163 + }, 164 + }); 165 + my $indexed_resolved = $proxy->_resolve_local_post_uri($index_context, "at://$did/app.bsky.feed.post/present-post"); 166 + is($indexed_resolved, $resolved, 'local post lookup can reuse the request local-post index'); 167 + is($store->{get_record_calls}{"$did|app.bsky.feed.post|present-post"}, 2, 'indexed local post lookup avoids another record fetch'); 168 + 169 + $store->{records}{"$did|app.bsky.actor.profile|self"} = { 170 + collection => 'app.bsky.actor.profile', 171 + rkey => 'self', 172 + cid => 'bafyreiprofile', 173 + value => { displayName => 'Alice Example' }, 174 + }; 175 + my $profile_first = $proxy->_profile_record_value($c, $store->{accounts_by_did}{$did}); 176 + my $profile_second = $proxy->_profile_record_value($c, $store->{accounts_by_did}{$did}); 177 + is($profile_first, $profile_second, 'repeat profile lookup reuses the cached profile value'); 178 + is($store->{get_record_calls}{"$did|app.bsky.actor.profile|self"}, 1, 'repeat profile lookup avoids another record fetch'); 149 179 150 180 eval { $proxy->_resolve_local_post_uri($c, "at://$did/app.bsky.feed.post/missing-post") }; 151 181 my $missing = $@;