Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use redis to cache results #972

Merged
merged 29 commits into from
Jun 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8f2b746
initial working
EDsCODE Jun 9, 2020
acc3588
Merge branch 'master' into function-caching
EDsCODE Jun 9, 2020
322efd4
small adjustments and make tests run
EDsCODE Jun 9, 2020
86f9d1f
working cache
EDsCODE Jun 9, 2020
a1650a9
edited arg handling in actions
EDsCODE Jun 9, 2020
a834446
caching dashboard trend items with periodic update
EDsCODE Jun 9, 2020
d68c8ce
funnel and trend cache working
EDsCODE Jun 10, 2020
d555e74
update intervals
EDsCODE Jun 10, 2020
d0e6778
update tests
EDsCODE Jun 10, 2020
7da4cef
updated types and funnel refersh logic
EDsCODE Jun 10, 2020
d9c2555
Merge branch 'master' into function-caching
EDsCODE Jun 10, 2020
d7342ec
refresh working
EDsCODE Jun 10, 2020
0b42e89
Merge branch 'function-caching' of https://github.com/PostHog/posthog…
EDsCODE Jun 10, 2020
843d85a
refresh dashboart item logic
EDsCODE Jun 10, 2020
d140e6a
refresh from dashboard working for trends components
EDsCODE Jun 10, 2020
b2be0a7
refresh from dashboard working for funnels and funnel refactored to hook
EDsCODE Jun 10, 2020
754662f
dashboard item reload
EDsCODE Jun 10, 2020
681835c
let deleted items run stale
EDsCODE Jun 10, 2020
c5adb43
fix tests
EDsCODE Jun 10, 2020
28502ea
tweak decorator logic
EDsCODE Jun 10, 2020
0312c5c
Merge branch 'master' into function-caching
EDsCODE Jun 10, 2020
7040cc6
update datetime format and default expiry
EDsCODE Jun 11, 2020
18a4df3
add team to cache signature
EDsCODE Jun 12, 2020
1e1b59f
make sure different workers pick up task
EDsCODE Jun 12, 2020
8355a03
use tooltip and change dashboard refresh order
EDsCODE Jun 15, 2020
9ded0ea
fix redundant calls and cache overwriting
EDsCODE Jun 15, 2020
15ec667
merge master and use team_id
EDsCODE Jun 15, 2020
63be9bd
updated types
EDsCODE Jun 15, 2020
18d8c30
Show tooltip when never refreshed
timgl Jun 15, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions frontend/src/lib/hooks/usePrevious.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useRef, useEffect } from 'react'

export function usePrevious(value) {
const ref = useRef()
useEffect(() => {
ref.current = value
})
return ref.current
}
36 changes: 33 additions & 3 deletions frontend/src/scenes/dashboard/DashboardItem.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Link } from 'lib/components/Link'
import { Dropdown, Menu } from 'antd'
import { useActions, useValues } from 'kea'
import { Dropdown, Menu, Tooltip } from 'antd'
import { combineUrl, router } from 'kea-router'
import { deleteWithUndo, Loading } from 'lib/utils'
import React from 'react'
import React, { useEffect, useState } from 'react'
import { ActionsLineGraph } from 'scenes/trends/ActionsLineGraph'
import { ActionsTable } from 'scenes/trends/ActionsTable'
import { ActionsPie } from 'scenes/trends/ActionsPie'
Expand All @@ -19,9 +20,14 @@ import {
BlockOutlined,
CopyOutlined,
DeliveredProcedureOutlined,
ReloadOutlined,
} from '@ant-design/icons'
import { dashboardColorNames, dashboardColors } from 'lib/colors'
import { useLongPress } from 'lib/hooks/useLongPress'
import { usePrevious } from 'lib/hooks/usePrevious'
import moment from 'moment'
import { trendsLogic } from 'scenes/trends/trendsLogic'
import { funnelVizLogic } from 'scenes/funnels/funnelVizLogic'

const typeMap = {
ActionsLineGraph: {
Expand Down Expand Up @@ -81,7 +87,9 @@ export function DashboardItem({
dashboards,
enableWobblyDragging,
index,
onRefresh,
}) {
const [initialLoaded, setInitialLoaded] = useState(false)
const className = typeMap[item.type].className
const Element = typeMap[item.type].element
const Icon = typeMap[item.type].icon
Expand All @@ -97,6 +105,18 @@ export function DashboardItem({
exclude: 'table, table *',
})

const filters = { ...item.filters, from_dashboard: item.id }
const logicProps = { dashboardItemId: item.id, filters: filters }
const { loadResults } = useActions(className === 'funnel' ? funnelVizLogic(logicProps) : trendsLogic(logicProps))
const { resultsLoading } = useValues(className === 'funnel' ? funnelVizLogic(logicProps) : trendsLogic(logicProps))
const previousLoading = usePrevious(resultsLoading)

// if a load is performed and returns that is not the initial load, we refresh dashboard item to update timestamp
useEffect(() => {
if (previousLoading && !resultsLoading && !initialLoaded) setInitialLoaded(true)
else if (previousLoading && !resultsLoading && initialLoaded) onRefresh()
}, [resultsLoading])

return (
<div
key={item.id}
Expand All @@ -122,6 +142,16 @@ export function DashboardItem({
</Link>
</div>
<div className="dashboard-item-settings">
<Tooltip
title={
<i> Refreshed: {item.last_refresh ? moment(item.last_refresh).fromNow() : 'never'}</i>
}
>
<ReloadOutlined
style={{ cursor: 'pointer', marginTop: -3 }}
onClick={() => loadResults(true)}
/>
</Tooltip>
<Dropdown
placement="bottomRight"
trigger="click"
Expand Down Expand Up @@ -213,7 +243,7 @@ export function DashboardItem({
<div className="graph-container">
<Element
dashboardItemId={item.id}
filters={item.filters}
filters={filters}
color={color}
theme={color === 'white' ? 'light' : 'dark'}
/>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/scenes/dashboard/DashboardItems.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export function DashboardItems({ logic }) {
const {
loadDashboardItems,
renameDashboardItem,
refreshDashboardItem,
updateLayouts,
updateItemColor,
duplicateDashboardItem,
Expand Down Expand Up @@ -84,6 +85,7 @@ export function DashboardItems({ logic }) {
dashboards={dashboards}
enableWobblyDragging={draggingEnabled !== 'off' ? noop : enableWobblyDragging}
index={index}
onRefresh={() => refreshDashboardItem(item.id)}
/>
</div>
))}
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/scenes/dashboard/dashboardLogic.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const dashboardLogic = kea({
enableDragging: true,
enableWobblyDragging: true,
disableDragging: true,
refreshDashboardItem: id => ({ id }),
}),

loaders: ({ props }) => ({
Expand Down Expand Up @@ -332,5 +333,9 @@ export const dashboardLogic = kea({
cache.draggingToastId = null
}
},
refreshDashboardItem: async ({ id }) => {
const dashboardItem = await api.get(`api/dashboard_item/${id}`)
EDsCODE marked this conversation as resolved.
Show resolved Hide resolved
dashboardsModel.actions.updateDashboardItem(dashboardItem)
},
}),
})
107 changes: 55 additions & 52 deletions frontend/src/scenes/funnels/FunnelViz.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,81 @@
import React, { Component } from 'react'
import api from 'lib/api'
import React, { useRef, useEffect, useState } from 'react'
import FunnelGraph from 'funnel-graph-js'
import { Link } from 'lib/components/Link'
import { Loading } from 'lib/utils'
import PropTypes from 'prop-types'
import { useValues, useActions } from 'kea'
import { funnelVizLogic } from 'scenes/funnels/funnelVizLogic'

export class FunnelViz extends Component {
container = React.createRef()
graphContainer = React.createRef()
constructor(props) {
super(props)
export function FunnelViz({ funnel: funnelParam, dashboardItemId, filters: filtersParam }) {
const container = useRef()
const [funnel, setFunnel] = useState(funnelParam)
const { results: funnelResult, resultsLoading: funnelLoading } = useValues(
funnelVizLogic({ filters: filtersParam, dashboardItemId })
)
const { loadResults: loadFunnel } = useActions(funnelVizLogic({ filters: filtersParam, dashboardItemId }))

this.state = {
funnel: props.funnel,
}
this.buildChart = this.buildChart.bind(this)
if (!props.funnel) this.fetchFunnel.call(this)
}
componentDidMount() {
if (this.props.funnel) this.buildChart()
window.addEventListener('resize', this.buildChart)
}
componentWillUnmount() {
window.removeEventListener('resize', this.buildChart)
}
fetchFunnel() {
api.get('api/funnel/' + this.props.filters.funnel_id).then(funnel => this.setState({ funnel }, this.buildChart))
}
componentDidUpdate(prevProps) {
if (prevProps.funnel !== this.props.funnel && this.state.funnel) {
this.setState({ funnel: this.props.funnel }, this.buildChart)
}
}
buildChart() {
if (!this.state.funnel || this.state.funnel.steps.length == 0) return
if (this.container.current) this.container.current.innerHTML = ''
function buildChart() {
if (!funnel || funnel.steps.length == 0) return
if (container.current) container.current.innerHTML = ''
let graph = new FunnelGraph({
container: '.funnel-graph',
data: {
labels: this.state.funnel.steps.map(step => `${step.name} (${step.count})`),
values: this.state.funnel.steps.map(step => step.count),
labels: funnel.steps.map(step => `${step.name} (${step.count})`),
values: funnel.steps.map(step => step.count),
colors: ['#66b0ff', 'var(--blue)'],
},
displayPercent: true,
})
graph.createContainer = () => {}
graph.container = this.container.current
graph.container = container.current
graph.graphContainer = document.createElement('div')
graph.graphContainer.classList.add('svg-funnel-js__container')
graph.container.appendChild(graph.graphContainer)

graph.draw()
}
render() {
let { funnel } = this.state
return funnel ? (
funnel.steps.length > 0 ? (
<div
data-attr="funnel-viz"
ref={this.container}
className="svg-funnel-js"
style={{ height: '100%', width: '100%' }}
></div>
) : (
<p style={{ margin: '1rem' }}>
This funnel doesn't have any steps.{' '}
<Link to={'/funnel/' + funnel.id}>Click here to add some steps.</Link>
</p>
)

useEffect(() => {
if (funnel) buildChart()
else loadFunnel()

window.addEventListener('resize', buildChart)
return window.removeEventListener('resize', buildChart)
}, [])

useEffect(() => {
buildChart()
}, [funnel])

useEffect(() => {
setFunnel(funnelParam)
}, [funnelParam])

useEffect(() => {
if (funnelResult) {
setFunnel(funnelResult)
}
}, [funnelResult])

return funnel && !funnelLoading ? (
funnel.steps.length > 0 ? (
<div
data-attr="funnel-viz"
ref={container}
className="svg-funnel-js"
style={{ height: '100%', width: '100%' }}
></div>
) : (
<Loading />
<p style={{ margin: '1rem' }}>
This funnel doesn't have any steps.{' '}
<Link to={'/funnel/' + funnel.id}>Click here to add some steps.</Link>
</p>
)
}
) : (
<Loading />
)
}

FunnelViz.propTypes = {
funnel: PropTypes.object,
filters: PropTypes.shape({ funnel_id: PropTypes.number }),
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/scenes/funnels/funnelLogic.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const funnelLogic = kea({
},
],
stepsWithCount: {
loadStepsWithCount: async (id = props.id) => {
return (await api.get('api/funnel/' + id)).steps
loadStepsWithCount: async ({ id, refresh }) => {
return (await api.get('api/funnel/' + id + (refresh ? '/?refresh=true' : ''))).steps
},
},
people: {
Expand Down Expand Up @@ -75,11 +75,11 @@ export const funnelLogic = kea({
if (update) actions.updateFunnel(values.funnel)
},
updateFunnelSuccess: async ({ funnel }) => {
actions.loadStepsWithCount(funnel.id)
actions.loadStepsWithCount({ id: funnel.id, refresh: true })
toast('Funnel saved!')
},
createFunnelSuccess: ({ funnel }) => {
actions.loadStepsWithCount(funnel.id)
actions.loadStepsWithCount({ id: funnel.id, refresh: true })
toast('Funnel saved!')
},
}),
Expand All @@ -88,13 +88,13 @@ export const funnelLogic = kea({
createFunnelSuccess: ({ funnel }) => `/funnel/${funnel.id}`,
}),

events: ({ actions, key }) => ({
events: ({ actions, key, props }) => ({
afterMount: () => {
if (key === 'new') {
return
}
actions.loadFunnel()
actions.loadStepsWithCount()
actions.loadStepsWithCount({ id: props.id })
},
}),
})
21 changes: 21 additions & 0 deletions frontend/src/scenes/funnels/funnelVizLogic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { kea } from 'kea'
import api from 'lib/api'

export const funnelVizLogic = kea({
key: props => props.dashboardItemId || 'some_funnel',
loaders: ({ props }) => ({
results: {
loadResults: async (refresh = false) => {
const { funnel_id, from_dashboard } = props.filters
let response = await api.get(
'api/funnel/' +
funnel_id +
'/?' +
(refresh ? 'refresh=true' : '') +
(from_dashboard ? '&from_dashboard=' + from_dashboard : '')
)
return response
},
},
}),
})
4 changes: 2 additions & 2 deletions frontend/src/scenes/trends/ActionsLineGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import { useActions, useValues } from 'kea'
import { trendsLogic } from 'scenes/trends/trendsLogic'

export function ActionsLineGraph({ dashboardItemId = null, color = 'white', filters: filtersParam }) {
const { filters, results } = useValues(trendsLogic({ dashboardItemId, filters: filtersParam }))
const { filters, results, resultsLoading } = useValues(trendsLogic({ dashboardItemId, filters: filtersParam }))
const { loadResults, loadPeople } = useActions(trendsLogic({ dashboardItemId, filters: filtersParam }))

const { people_action, people_day, ...otherFilters } = filters

useEffect(() => {
loadResults()
}, [toParams(otherFilters)])
return results ? (
return results && !resultsLoading ? (
filters.session || results.reduce((total, item) => total + item.count, 0) > 0 ? (
<LineGraph
data-attr="trend-line-graph"
Expand Down
22 changes: 15 additions & 7 deletions frontend/src/scenes/trends/ActionsPie.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import React, { useEffect, useState } from 'react'
import api from 'lib/api'
import { Loading, toParams } from 'lib/utils'
import { Loading } from 'lib/utils'
import { LineGraph } from './LineGraph'
import { getChartColors } from 'lib/colors'
import { useValues, useActions } from 'kea'
import { trendsLogic } from 'scenes/trends/trendsLogic'

export function ActionsPie({ filters, color }) {
export function ActionsPie({ dashboardItemId, filters: filtersParam, color }) {
const [data, setData] = useState(null)
const [total, setTotal] = useState(0)

async function fetchGraph() {
const data = await api.get('api/action/trends/?' + toParams(filters))
const { filters, results, resultsLoading } = useValues(trendsLogic({ dashboardItemId, filters: filtersParam }))
const { loadResults } = useActions(trendsLogic({ dashboardItemId, filters: filtersParam }))

function updateData() {
const data = results
data.sort((a, b) => b.count - a.count)

const colorList = getChartColors(color)
Expand All @@ -30,10 +34,14 @@ export function ActionsPie({ filters, color }) {
}

useEffect(() => {
fetchGraph()
loadResults()
}, [filters, color])

return data ? (
useEffect(() => {
updateData()
}, [results])

return data && !resultsLoading ? (
data[0] && data[0].labels ? (
<div
style={{
Expand Down
Loading