Caching heuristics (#1938)

* Tempfix profile load

* First pass at staleTime
zio/stable
Eric Bailey 2023-11-16 17:28:50 -06:00 committed by GitHub
parent f89dc63801
commit 6dfb2a232f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 122 additions and 22 deletions

View File

@ -5,6 +5,7 @@ import {useQuery, useQueryClient} from '@tanstack/react-query'
import {logger} from '#/logger'
import {useSession} from '#/state/session'
import {useMyFollowsQuery} from '#/state/queries/my-follows'
import {STALE} from '#/state/queries'
export const RQKEY = (prefix: string) => ['actor-autocomplete', prefix]
@ -13,8 +14,7 @@ export function useActorAutocompleteQuery(prefix: string) {
const {data: follows, isFetching} = useMyFollowsQuery()
return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({
// cached for 1 min
staleTime: 60 * 1000,
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(prefix || ''),
async queryFn() {
const res = prefix
@ -41,8 +41,7 @@ export function useActorAutocompleteFn() {
if (query) {
try {
res = await queryClient.fetchQuery({
// cached for 1 min
staleTime: 60 * 1000,
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(query || ''),
queryFn: () =>
agent.searchActorsTypeahead({

View File

@ -1,12 +1,15 @@
import {ComAtprotoServerCreateAppPassword} from '@atproto/api'
import {useQuery, useQueryClient, useMutation} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
export const RQKEY = () => ['app-passwords']
export function useAppPasswordsQuery() {
const {agent} = useSession()
return useQuery({
staleTime: STALE.INFINITY,
queryKey: RQKEY(),
queryFn: async () => {
const res = await agent.com.atproto.server.listAppPasswords({})

View File

@ -20,6 +20,7 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {useSession} from '#/state/session'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {STALE} from '#/state/queries'
export type FeedSourceFeedInfo = {
type: 'feed'
@ -139,6 +140,7 @@ export function useFeedSourceInfoQuery({uri}: {uri: string}) {
const type = getFeedTypeFromUri(uri)
return useQuery({
staleTime: STALE.INFINITY,
queryKey: feedSourceInfoQueryKey({uri}),
queryFn: async () => {
let view: FeedSourceInfo

View File

@ -2,6 +2,7 @@ import React from 'react'
import {useQueryClient, useMutation} from '@tanstack/react-query'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const fetchHandleQueryKey = (handleOrDid: string) => ['handle', handleOrDid]
const fetchDidQueryKey = (handleOrDid: string) => ['did', handleOrDid]
@ -14,6 +15,7 @@ export function useFetchHandle() {
async (handleOrDid: string) => {
if (handleOrDid.startsWith('did:')) {
const res = await queryClient.fetchQuery({
staleTime: STALE.MINUTES.FIVE,
queryKey: fetchHandleQueryKey(handleOrDid),
queryFn: () => agent.getProfile({actor: handleOrDid}),
})
@ -27,11 +29,17 @@ export function useFetchHandle() {
export function useUpdateHandleMutation() {
const {agent} = useSession()
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({handle}: {handle: string}) => {
await agent.updateHandle({handle})
},
onSuccess(_data, variables) {
queryClient.invalidateQueries({
queryKey: fetchHandleQueryKey(variables.handle),
})
},
})
}
@ -42,6 +50,7 @@ export function useFetchDid() {
return React.useCallback(
async (handleOrDid: string) => {
return queryClient.fetchQuery({
staleTime: STALE.INFINITY,
queryKey: fetchDidQueryKey(handleOrDid),
queryFn: async () => {
let identifier = handleOrDid

View File

@ -3,3 +3,14 @@ import {BskyAgent} from '@atproto/api'
export const PUBLIC_BSKY_AGENT = new BskyAgent({
service: 'https://api.bsky.app',
})
export const STALE = {
MINUTES: {
ONE: 1e3 * 60,
FIVE: 1e3 * 60 * 5,
},
HOURS: {
ONE: 1e3 * 60 * 60,
},
INFINITY: Infinity,
}

View File

@ -2,6 +2,7 @@ import {ComAtprotoServerDefs} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
function isInviteAvailable(invite: ComAtprotoServerDefs.InviteCode): boolean {
return invite.available - invite.uses.length > 0 && !invite.disabled
@ -15,6 +16,7 @@ export function useInviteCodesQuery() {
const {agent} = useSession()
return useQuery({
staleTime: STALE.HOURS.ONE,
queryKey: ['inviteCodes'],
queryFn: async () => {
const res = await agent.com.atproto.server.getAccountInviteCodes({})

View File

@ -1,6 +1,8 @@
import {AppBskyGraphGetList} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
@ -16,6 +18,7 @@ export function useListMembersQuery(uri: string) {
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(uri),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getList({

View File

@ -16,8 +16,10 @@
import {AtUri} from '@atproto/api'
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
import {useSession} from '../session'
import {RQKEY as LIST_MEMBERS_RQKEY} from './list-members'
import {useSession} from '#/state/session'
import {RQKEY as LIST_MEMBERS_RQKEY} from '#/state/queries/list-members'
import {STALE} from '#/state/queries'
// sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records
const SANITY_PAGE_LIMIT = 1000
@ -38,6 +40,7 @@ export interface ListMembersip {
export function useDangerousListMembershipsQuery() {
const {agent, currentAccount} = useSession()
return useQuery<ListMembersip[]>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(),
async queryFn() {
if (!currentAccount) {

View File

@ -13,12 +13,14 @@ import {invalidate as invalidateMyLists} from './my-lists'
import {RQKEY as PROFILE_LISTS_RQKEY} from './profile-lists'
import {uploadBlob} from '#/lib/api'
import {until} from '#/lib/async/until'
import {STALE} from '#/state/queries'
export const RQKEY = (uri: string) => ['list', uri]
export function useListQuery(uri?: string) {
const {agent} = useSession()
return useQuery<AppBskyGraphDefs.ListView, Error>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(uri || ''),
async queryFn() {
if (!uri) {

View File

@ -1,6 +1,8 @@
import {AppBskyGraphGetBlocks} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
export const RQKEY = () => ['my-blocked-accounts']
type RQPageParam = string | undefined
@ -14,6 +16,7 @@ export function useMyBlockedAccountsQuery() {
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getBlocks({

View File

@ -1,6 +1,7 @@
import {AppBskyActorDefs} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {useSession} from '../session'
import {STALE} from '#/state/queries'
// sanity limit is SANITY_PAGE_LIMIT*PAGE_SIZE total records
const SANITY_PAGE_LIMIT = 1000
@ -12,6 +13,7 @@ export const RQKEY = () => ['my-follows']
export function useMyFollowsQuery() {
const {agent, currentAccount} = useSession()
return useQuery<AppBskyActorDefs.ProfileViewBasic[]>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(),
async queryFn() {
if (!currentAccount) {

View File

@ -1,7 +1,9 @@
import {AppBskyGraphDefs} from '@atproto/api'
import {useQuery, QueryClient} from '@tanstack/react-query'
import {accumulate} from 'lib/async/accumulate'
import {useSession} from '../session'
import {accumulate} from '#/lib/async/accumulate'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
export type MyListsFilter = 'all' | 'curate' | 'mod'
export const RQKEY = (filter: MyListsFilter) => ['my-lists', filter]
@ -9,6 +11,7 @@ export const RQKEY = (filter: MyListsFilter) => ['my-lists', filter]
export function useMyListsQuery(filter: MyListsFilter) {
const {agent, currentAccount} = useSession()
return useQuery<AppBskyGraphDefs.ListView[]>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(filter),
async queryFn() {
let lists: AppBskyGraphDefs.ListView[] = []

View File

@ -1,6 +1,8 @@
import {AppBskyGraphGetMutes} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
export const RQKEY = () => ['my-muted-accounts']
type RQPageParam = string | undefined
@ -14,6 +16,7 @@ export function useMyMutedAccountsQuery() {
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getMutes({

View File

@ -12,6 +12,7 @@ import {useSession} from '../../session'
import {useModerationOpts} from '../preferences'
import {shouldFilterNotif} from './util'
import {useMutedThreads} from '#/state/muted-threads'
import {STALE} from '#/state/queries'
const GROUPABLE_REASONS = ['like', 'repost', 'follow']
const PAGE_SIZE = 30
@ -60,6 +61,7 @@ export function useNotificationFeedQuery(opts?: {enabled?: boolean}) {
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.listNotifications({

View File

@ -13,6 +13,7 @@ import {ListFeedAPI} from 'lib/api/feed/list'
import {MergeFeedAPI} from 'lib/api/feed/merge'
import {useModerationOpts} from '#/state/queries/preferences'
import {logger} from '#/logger'
import {STALE} from '#/state/queries'
type ActorDid = string
type AuthorFilter =
@ -132,6 +133,7 @@ export function usePostFeedQuery(
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(feedDesc, params),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
console.log('fetch', feedDesc, pageParam)

View File

@ -1,10 +1,13 @@
import {AppBskyFeedGetLikes} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
// TODO refactor invalidate on mutate?
export const RQKEY = (resolvedUri: string) => ['post-liked-by', resolvedUri]
export function usePostLikedByQuery(resolvedUri: string | undefined) {
@ -16,6 +19,7 @@ export function usePostLikedByQuery(resolvedUri: string | undefined) {
QueryKey,
RQPageParam
>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(resolvedUri || ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.getLikes({

View File

@ -1,10 +1,13 @@
import {AppBskyFeedGetRepostedBy} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
// TODO refactor invalidate on mutate?
export const RQKEY = (resolvedUri: string) => ['post-reposted-by', resolvedUri]
export function usePostRepostedByQuery(resolvedUri: string | undefined) {
@ -16,6 +19,7 @@ export function usePostRepostedByQuery(resolvedUri: string | undefined) {
QueryKey,
RQPageParam
>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(resolvedUri || ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.getRepostedBy({

View File

@ -4,8 +4,10 @@ import {
AppBskyFeedGetPostThread,
} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {useSession} from '#/state/session'
import {UsePreferencesQueryResponse} from '#/state/queries/preferences/types'
import {STALE} from '#/state/queries'
export const RQKEY = (uri: string) => ['post-thread', uri]
type ThreadViewNode = AppBskyFeedGetPostThread.OutputSchema['thread']
@ -58,6 +60,7 @@ export type ThreadNode =
export function usePostThreadQuery(uri: string | undefined) {
const {agent} = useSession()
return useQuery<ThreadNode, Error>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(uri || ''),
async queryFn() {
const res = await agent.getPostThread({uri: uri!})

View File

@ -1,14 +1,17 @@
import React from 'react'
import {AppBskyFeedDefs, AtUri} from '@atproto/api'
import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query'
import {useSession} from '../session'
import {updatePostShadow} from '../cache/post-shadow'
import {useSession} from '#/state/session'
import {updatePostShadow} from '#/state/cache/post-shadow'
import {STALE} from '#/state/queries'
export const RQKEY = (postUri: string) => ['post', postUri]
export function usePostQuery(uri: string | undefined) {
const {agent} = useSession()
return useQuery<AppBskyFeedDefs.PostView>({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(uri || ''),
async queryFn() {
const res = await agent.getPosts({uris: [uri!]})
@ -28,6 +31,7 @@ export function useGetPost() {
return React.useCallback(
async ({uri}: {uri: string}) => {
return queryClient.fetchQuery({
staleTime: STALE.MINUTES.ONE,
queryKey: RQKEY(uri || ''),
async queryFn() {
const urip = new AtUri(uri)

View File

@ -22,6 +22,7 @@ import {
DEFAULT_THREAD_VIEW_PREFS,
} from '#/state/queries/preferences/const'
import {getModerationOpts} from '#/state/queries/preferences/moderation'
import {STALE} from '#/state/queries'
export * from '#/state/queries/preferences/types'
export * from '#/state/queries/preferences/moderation'
@ -33,6 +34,7 @@ export function usePreferencesQuery() {
const {agent, hasSession} = useSession()
return useQuery({
enabled: hasSession,
staleTime: STALE.INFINITY,
queryKey: usePreferencesQueryKey,
queryFn: async () => {
const res = await agent.getPreferences()

View File

@ -1,6 +1,9 @@
import {useQuery} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
// TODO refactor invalidate on mutate?
export const RQKEY = (did: string) => ['profile-extra-info', did]
/**
@ -10,6 +13,7 @@ export const RQKEY = (did: string) => ['profile-extra-info', did]
export function useProfileExtraInfoQuery(did: string) {
const {agent} = useSession()
return useQuery({
staleTime: STALE.INFINITY,
queryKey: RQKEY(did),
async queryFn() {
const [listsRes, feedsRes] = await Promise.all([

View File

@ -1,10 +1,13 @@
import {AppBskyFeedGetActorFeeds} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
// TODO refactor invalidate on mutate?
export const RQKEY = (did: string) => ['profile-feedgens', did]
export function useProfileFeedgensQuery(
@ -20,6 +23,7 @@ export function useProfileFeedgensQuery(
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(did),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.feed.getActorFeeds({

View File

@ -1,6 +1,8 @@
import {AppBskyGraphGetFollowers} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
@ -16,6 +18,7 @@ export function useProfileFollowersQuery(did: string | undefined) {
QueryKey,
RQPageParam
>({
staleTime: STALE.MINUTES.FIVE,
queryKey: RQKEY(did || ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getFollowers({

View File

@ -1,10 +1,13 @@
import {AppBskyGraphGetFollows} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
// TODO refactor invalidate on mutate?
export const RQKEY = (did: string) => ['profile-follows', did]
export function useProfileFollowsQuery(did: string | undefined) {
@ -16,6 +19,7 @@ export function useProfileFollowsQuery(did: string | undefined) {
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(did || ''),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getFollows({

View File

@ -1,6 +1,8 @@
import {AppBskyGraphGetLists} from '@atproto/api'
import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
const PAGE_SIZE = 30
type RQPageParam = string | undefined
@ -17,6 +19,7 @@ export function useProfileListsQuery(did: string, opts?: {enabled?: boolean}) {
QueryKey,
RQPageParam
>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(did),
async queryFn({pageParam}: {pageParam: RQPageParam}) {
const res = await agent.app.bsky.graph.getLists({

View File

@ -16,12 +16,14 @@ import {Shadow} from '#/state/cache/types'
import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
import {RQKEY as RQKEY_MY_MUTED} from './my-muted-accounts'
import {RQKEY as RQKEY_MY_BLOCKED} from './my-blocked-accounts'
import {STALE} from '#/state/queries'
export const RQKEY = (did: string) => ['profile', did]
export function useProfileQuery({did}: {did: string | undefined}) {
const {agent} = useSession()
return useQuery({
staleTime: STALE.MINUTES.FIVE,
queryKey: RQKEY(did || ''),
queryFn: async () => {
const res = await agent.getProfile({actor: did || ''})
@ -304,6 +306,7 @@ function useProfileMuteMutation() {
function useProfileUnmuteMutation() {
const {agent} = useSession()
const queryClient = useQueryClient()
return useMutation<void, Error, {did: string; skipOptimistic?: boolean}>({
mutationFn: async ({did}) => {
await agent.unmute(did)
@ -316,6 +319,9 @@ function useProfileUnmuteMutation() {
})
}
},
onSuccess() {
queryClient.invalidateQueries({queryKey: RQKEY_MY_MUTED()})
},
onError(error, variables) {
if (!variables.skipOptimistic) {
// revert the optimistic update

View File

@ -1,12 +1,15 @@
import {useQuery} from '@tanstack/react-query'
import {AtUri} from '@atproto/api'
import {useSession} from '../session'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
export const RQKEY = (uri: string) => ['resolved-uri', uri]
export function useResolveUriQuery(uri: string | undefined) {
const {agent} = useSession()
return useQuery<{uri: string; did: string}, Error>({
staleTime: STALE.INFINITY,
queryKey: RQKEY(uri || ''),
async queryFn() {
const urip = new AtUri(uri || '')

View File

@ -1,10 +1,13 @@
import {BskyAgent} from '@atproto/api'
import {useQuery} from '@tanstack/react-query'
import {STALE} from '#/state/queries'
export const RQKEY = (serviceUrl: string) => ['service', serviceUrl]
export function useServiceQuery(serviceUrl: string) {
return useQuery({
staleTime: STALE.HOURS.ONE,
queryKey: RQKEY(serviceUrl),
queryFn: async () => {
const agent = new BskyAgent({service: serviceUrl})

View File

@ -2,6 +2,7 @@ import {useInfiniteQuery, InfiniteData, QueryKey} from '@tanstack/react-query'
import {AppBskyFeedGetSuggestedFeeds} from '@atproto/api'
import {useSession} from '#/state/session'
import {STALE} from '#/state/queries'
export const suggestedFeedsQueryKey = ['suggestedFeeds']
@ -15,6 +16,7 @@ export function useSuggestedFeedsQuery() {
QueryKey,
string | undefined
>({
staleTime: STALE.INFINITY,
queryKey: suggestedFeedsQueryKey,
queryFn: async ({pageParam}) => {
const res = await agent.app.bsky.feed.getSuggestedFeeds({

View File

@ -14,6 +14,7 @@ import {
import {useSession} from '#/state/session'
import {useModerationOpts} from '#/state/queries/preferences'
import {STALE} from '#/state/queries'
const suggestedFollowsQueryKey = ['suggested-follows']
const suggestedFollowsByActorQueryKey = (did: string) => [
@ -33,6 +34,7 @@ export function useSuggestedFollowsQuery() {
string | undefined
>({
enabled: !!moderationOpts,
staleTime: STALE.INFINITY,
queryKey: suggestedFollowsQueryKey,
queryFn: async ({pageParam}) => {
const res = await agent.app.bsky.actor.getSuggestions({