Skip to content

Commit

Permalink
Cache bet points longer
Browse files Browse the repository at this point in the history
  • Loading branch information
IanPhilips committed Feb 28, 2025
1 parent e707d1b commit 094a9b7
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 41 deletions.
12 changes: 11 additions & 1 deletion backend/api/src/get-bets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import { getUserIdFromUsername } from 'shared/supabase/users'
import { getBetsWithFilter } from 'shared/supabase/bets'
import { convertBet, NON_POINTS_BETS_LIMIT } from 'common/supabase/bets'
import { filterDefined } from 'common/util/array'
import { ValidatedAPIParams } from 'common/api/schema'

export const getBets: APIHandler<'bets'> = async (props) => {
export const getBetsInternal = async (props: ValidatedAPIParams<'bets'>) => {
const {
limit,
username,
Expand Down Expand Up @@ -90,6 +91,15 @@ export const getBets: APIHandler<'bets'> = async (props) => {
return await getBetsWithFilter(pg, opts)
}

export const getBets: APIHandler<'bets'> = async (props) =>
getBetsInternal(props)

export const getBetPointsBetween: APIHandler<'bet-points'> = async (props) =>
getBetsInternal({
...props,
points: true,
})

async function getBetTime(pg: SupabaseDirectClient, id: string) {
const created = await pg.oneOrNone(
`
Expand Down
3 changes: 2 additions & 1 deletion backend/api/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { pinComment } from './pin-comment'
import { getManagrams } from './get-managrams'
import { getGroups } from './get-groups'
import { getComments } from './get-comments'
import { getBets } from './get-bets'
import { getBetPointsBetween, getBets } from './get-bets'
import { getLiteUser, getUser } from './get-user'
import { getUsers } from './get-users'
import { getUserBalancesByIds, getUsersByIds } from './get-users-by-ids'
Expand Down Expand Up @@ -181,6 +181,7 @@ export const handlers: { [k in APIPath]: APIHandler<k> } = {
'bet/cancel/:betId': cancelBet,
'market/:contractId/sell': sellShares,
bets: getBets,
'bet-points': getBetPointsBetween,
'get-notifications': getNotifications,
'get-channel-memberships': getChannelMemberships,
'get-channel-messages': getChannelMessages,
Expand Down
18 changes: 18 additions & 0 deletions common/src/api/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,24 @@ export const API = (_apiTypeCheck = {
})
.strict(),
},
'bet-points': {
method: 'GET',
visibility: 'public',
authed: false,
cache: 'public, max-age=600, stale-while-revalidate=60',
returns: [] as Bet[],
props: z
.object({
contractId: z.string(),
answerId: z.string().optional(),
limit: z.coerce.number().gte(0).lte(50000).default(50000),
beforeTime: z.coerce.number(),
afterTime: z.coerce.number(),
filterRedemptions: coerceBoolean.optional(),
includeZeroShareRedemptions: coerceBoolean.optional(),
})
.strict(),
},
'unique-bet-group-count': {
method: 'GET',
visibility: 'undocumented',
Expand Down
25 changes: 25 additions & 0 deletions common/src/bets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,28 @@ export const getBetPoints = async (
}))
)
}

// gets random bets - 50,000 by default
export const getBetPointsBetween = async (options: APIParams<'bet-points'>) => {
const data = await unauthedApi('bet-points', options)

const sorted = sortBy(data, 'createdTime')

if (sorted.length === 0) return []

// we need to include previous prob for binary in case the prob shifted from something
const includePrevProb = !sorted[0].answerId

return buildArray(
includePrevProb && {
x: sorted[0].createdTime - 1,
y: sorted[0].probBefore,
answerId: sorted[0].answerId,
},
sorted.map((r) => ({
x: r.createdTime,
y: r.probAfter,
answerId: r.answerId,
}))
)
}
7 changes: 5 additions & 2 deletions common/src/contract-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
sortAnswers,
} from './answer'
import { getDashboardsToDisplayOnContract } from './supabase/dashboards'
import { getBetPoints, getTotalBetCount } from './bets'
import { getBetPointsBetween, getTotalBetCount } from './bets'

export async function getContractParams(
contract: Contract,
Expand Down Expand Up @@ -72,9 +72,12 @@ export async function getContractParams(
})
: ([] as Bet[]),
hasMechanism && !shouldHideGraph(contract)
? getBetPoints(contract.id, {
? getBetPointsBetween({
contractId: contract.id,
filterRedemptions: !includeRedemptions,
includeZeroShareRedemptions: includeRedemptions,
beforeTime: contract.lastBetTime ?? contract.createdTime,
afterTime: contract.createdTime,
})
: [],
getRecentTopLevelCommentsAndReplies(db, contract.id, 25),
Expand Down
87 changes: 50 additions & 37 deletions web/components/charts/contract/zoom-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import { buildArray } from 'common/util/array'
import { debounce } from 'lodash'
import { useCallback, useEffect, useLayoutEffect, useState } from 'react'
import { ScaleTime } from 'd3-scale'
import { getBetPoints } from 'common/bets'
import { getBetPointsBetween } from 'common/bets'
import { getMultiBetPoints } from 'common/contract-params'
import { MultiContract } from 'common/contract'

export async function getPointsBetween(
contractId: string,
min?: number,
max?: number
min: number,
max: number
) {
const points = await getBetPoints(contractId, {
filterRedemptions: true,
const points = await getBetPointsBetween({
contractId,
beforeTime: max,
afterTime: min,
filterRedemptions: true,
})

const compressed = maxMinBin(points, 500)
Expand All @@ -26,60 +27,67 @@ export async function getPointsBetween(
// only for single value contracts
export const useDataZoomFetcher = <T>(props: {
contractId: string
createdTime: number
lastBetTime: number
viewXScale?: ScaleTime<number, number>
points: HistoryPoint<T>[]
}) => {
const [data, setData] = useState(props.points)
const { contractId, createdTime, lastBetTime, viewXScale, points } = props
const [data, setData] = useState(points)
const [loading, setLoading] = useState(false)

const onZoomData = useCallback(
debounce(async (min?: number, max?: number) => {
debounce(async (min: number, max: number) => {
if (min && max) {
setLoading(true)
const points = await getPointsBetween(props.contractId, min, max)
const points = await getPointsBetween(contractId, min, max)

setData(
buildArray(
props.points.filter((p) => p.x <= min),
points.filter((p) => p.x <= min),
points,
props.points.filter((p) => p.x >= max)
points.filter((p) => p.x >= max)
).sort((a, b) => a.x - b.x)
)

setLoading(false)
} else {
setData(props.points)
setData(points)
}
}, 100),
[props.contractId]
[contractId]
)

useLayoutEffect(() => {
setData(props.points)
}, [props.contractId])
setData(points)
}, [contractId])

useEffect(() => {
if (props.viewXScale) {
const [minX, maxX] = props.viewXScale.range()
if (viewXScale) {
const [minX, maxX] = viewXScale.range()
if (Math.abs(minX - maxX) <= 1) return
// 20px buffer
const min = props.viewXScale.invert(minX - 20).valueOf()
const max = props.viewXScale.invert(maxX + 20).valueOf()
const min = viewXScale.invert(minX - 20).valueOf()
const max = viewXScale.invert(maxX + 20).valueOf()
const fixedMin = Math.max(min, createdTime)
const fixedMax = Math.min(max, lastBetTime)

onZoomData(min, max)
onZoomData(fixedMin, fixedMax)
} else {
onZoomData()
onZoomData(createdTime, lastBetTime)
}
}, [props.viewXScale])
}, [viewXScale])

return { points: data, loading }
}

export async function getMultichoicePointsBetween(
contract: MultiContract,
min?: number,
max?: number
min: number,
max: number
) {
const allBetPoints = await getBetPoints(contract.id, {
const allBetPoints = await getBetPointsBetween({
contractId: contract.id,
filterRedemptions: false,
includeZeroShareRedemptions: true,
beforeTime: max,
Expand All @@ -95,8 +103,10 @@ export const useMultiChoiceDataZoomFetcher = <T>(props: {
contract: MultiContract
viewXScale?: ScaleTime<number, number>
points: MultiPoints
createdTime: number
lastBetTime: number
}) => {
const { contract, points } = props
const { contract, points, createdTime, lastBetTime, viewXScale } = props
const [data, setData] = useState(points)
const [loading, setLoading] = useState(false)

Expand All @@ -115,13 +125,13 @@ export const useMultiChoiceDataZoomFetcher = <T>(props: {

// Get all answer IDs from both objects
const allAnswerIds = Array.from(
new Set([...Object.keys(props.points), ...Object.keys(zoomedPoints)])
new Set([...Object.keys(points), ...Object.keys(zoomedPoints)])
)

// Process each answer ID
allAnswerIds.forEach((answerId) => {
// Get points for this answer ID
const answerPoints = props.points[answerId] || []
const answerPoints = points[answerId] || []
const zoomedAnswerPoints = zoomedPoints[answerId] || []

// Convert serialized points to HistoryPoint objects
Expand All @@ -141,32 +151,35 @@ export const useMultiChoiceDataZoomFetcher = <T>(props: {
setData(newData)
setLoading(false)
} else {
setData(props.points)
setData(points)
}
}, 100),
[contract.id]
)

useLayoutEffect(() => {
setData(props.points)
if (Object.keys(props.points).length === 0 && props.viewXScale) {
const [minX, maxX] = props.viewXScale.range()
setData(points)
if (Object.keys(points).length === 0 && viewXScale) {
const [minX, maxX] = viewXScale.range()
onZoomData(minX, maxX)
}
}, [contract.id])

useEffect(() => {
if (props.viewXScale) {
const [minX, maxX] = props.viewXScale.range()
if (viewXScale) {
const [minX, maxX] = viewXScale.range()
if (Math.abs(minX - maxX) <= 1) return
// 20px buffer
const min = props.viewXScale.invert(minX - 20).valueOf()
const max = props.viewXScale.invert(maxX + 20).valueOf()
const min = viewXScale.invert(minX - 20).valueOf()
const max = viewXScale.invert(maxX + 20).valueOf()
const fixedMin = Math.max(min, createdTime)
const fixedMax = Math.min(max, lastBetTime)

onZoomData(min, max)
onZoomData(fixedMin, fixedMax)
} else {
onZoomData()
}
}, [props.viewXScale])
}, [viewXScale])

return { points: data, loading }
}
4 changes: 4 additions & 0 deletions web/components/contract/contract-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ export const BinaryOverview = (props: {

const { points, loading } = useDataZoomFetcher({
contractId: contract.id,
createdTime: contract.createdTime,
lastBetTime: contract.lastBetTime ?? contract.createdTime,
viewXScale: zoomParams?.viewXScale,
points: props.betPoints,
})
Expand Down Expand Up @@ -356,6 +358,8 @@ const ChoiceOverview = (props: {
contract,
viewXScale: zoomParams?.viewXScale,
points: props.points,
createdTime: contract.createdTime,
lastBetTime: contract.lastBetTime ?? contract.createdTime,
})
return (
<>
Expand Down

0 comments on commit 094a9b7

Please sign in to comment.