Skip to content

Add ability to make the FE trigger automatic dashboard refresh#6442

Open
RobertJoonas wants to merge 7 commits into
masterfrom
automatic-dashboard-refresh
Open

Add ability to make the FE trigger automatic dashboard refresh#6442
RobertJoonas wants to merge 7 commits into
masterfrom
automatic-dashboard-refresh

Conversation

@RobertJoonas

@RobertJoonas RobertJoonas commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Changes

Resurrecting what was started in #5362.

Adds a new plug in the :internal_stats_api pipeline that adds an x-api-version response header. Incrementing the value will trigger an automatic refresh. The version string is defined in the plug itself, and the same value is also passed into the FE via a <meta> tag. Without the meta tag in the DOM, the frontend defaults to EXPECTED_API_VERSION = 0, which:

  • keeps the FE test suite working without having to worry about the api version
  • makes sure that dashboard does not refresh when this PR itself is deployed

Tests

  • Automated tests have been added

Changelog

  • This PR does not make a user-facing change

Documentation

  • This change does not need a documentation update

Dark mode

  • This PR does not change the UI

- extract fetchSuggestions into a separate module to break circular dependency
- handle headers not existing in API response (no headers in tests)
@github-actions

Copy link
Copy Markdown
Preview environment👷🏼‍♀️🏗️
PR-6442

@RobertJoonas RobertJoonas changed the title Add ability to trigger automatic dashboard refresh when api version gets outdated Add ability to make the FE trigger automatic dashboard refresh Jun 10, 2026
Comment thread lib/plausible_web/plugs/internal_stats_api_version.ex
Comment on lines +21 to +22
def call(conn, _opts \\ nil) do
put_resp_header(conn, "x-api-version", @api_version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion, non-blocking: let's test that it's added to an internal stats endpoint like /query

}

async function handleApiResponse(response: Response) {
const currentApiVersion = response.headers?.get('x-api-version')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, non-blocking: I think a separate utility like getCurrentApiVersion would be appropriate, next to other code related to this feature.

}

async function handleApiResponse(response: Response) {
const currentApiVersion = response.headers?.get('x-api-version')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, non-blocking: when are response headers not defined?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Jest tests. I figured the tests shouldn't have to worry about providing empty response headers just in order to make this work.

currentApiVersion
)

console.log('API version mismatch detected, reloading...')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, non-blocking: the other redirect is notifying with console.warn

async function handleApiResponse(response: Response) {
const currentApiVersion = response.headers?.get('x-api-version')

if (currentApiVersion && currentApiVersion !== EXPECTED_API_VERSION) {

@apata apata Jun 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion, non-blocking: if we move the checks into maybeReloadForApiVersion, it can be better unit tested. The args for it can be windowLocation and responseHeaders

const currentApiVersion = response.headers?.get('x-api-version')

if (currentApiVersion && currentApiVersion !== EXPECTED_API_VERSION) {
maybeReloadForApiVersion(window.location, currentApiVersion)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue, non-blocking: I think we should reload on idempotent requests only, not when updating segments etc.

getLabel,
formattedFilters
} from '../../util/filters'
import { fetchSuggestions } from '../../util/fetch-suggestions'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question, blocking: is this change connected with the new feature?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of. In api.ts, importing from url-search-params.ts created a circular dependency:

  • filters.js imports from api.ts
  • api.ts imports from url-search-params.ts (added in this PR)
  • url-search-params.ts imports from url-search-params-v1.ts
  • url-search-params-v1.ts imports from filters.js

This gets rid of it by eliminating the first -- make filters.js not rely on api.ts

*
* BE: lib/plausible_web/plugs/internal_stats_api_version.ex
*/
export function maybeReloadForApiVersion(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue, blocking: I think this must be unit tested quite extensively.

The FE makes multiple requests for data concurrently. The requests may hit different instances of the BE. During rolling deploys, the BE instances may report different API versions. What should happen?

I suggest we spec it out with a jest test suite that includes the concurrent requests during rolling deploys scenario.

I suspect we may need to set a flag that we're about to update to have deterministic behavior, e.g. at module level, let reloadingDueToApiVersionChange

@RobertJoonas RobertJoonas Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During rolling deploys, the BE instances may report different API versions. What should happen?

Uff, yeah good catch! I feel like this is now turning into an infrastructure problem. So basically, a dashboard reload is useless until there's old code still around on at least one app server. We definitely don't want the dashboard to keep reloading itself until our deployment finishes.

I guess the version bump should happen separate from the code change then. I'm now thinking about storing the version in Postgres and bumping it via remote shell once all deployments have finished, maybe?

)
}

function addOrReplaceSearch(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue, blocking: This regex-based function is not encoding the value, so as a utility with such a general name, it's actually quite risky. Can we use the utils parseSearch and stringifySearch to create the new link?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants