Dashboard Data and Refresh
Dashboard widgets display data aggregated from multiple workspace collections. This page explains how that data flows from the database to your screen, how caching works, and how to optimize performance for large workspaces.
Data flow overview
The data pipeline for dashboard widgets follows this path:
- User opens a dashboard -- the frontend requests widget data from the API.
- API checks the cache -- Redis is checked for a recently computed result.
- Cache hit -- the cached result is returned immediately.
- Cache miss -- the API runs an aggregation query against MongoDB.
- Result is cached -- the fresh result is stored in Redis with a TTL.
- Data is returned -- the frontend renders the widget.
| Step | Component | Latency (typical) |
|---|---|---|
| Cache check | Redis | < 1ms |
| Cache hit response | API to frontend | 5-20ms |
| Aggregation query | MongoDB | 50-500ms depending on data volume |
| Cache write | Redis | < 1ms |
| Full round trip (cache miss) | End to end | 100-600ms |
Data sources
Each widget type draws data from specific MongoDB collections:
| Widget category | Primary collections | Secondary collections |
|---|---|---|
| Issue distribution (state, priority, label, assignee) | issues | states, labels, workspace_memberships |
| Activity widgets (heatmap, timeline) | activity_log | issues, workspace_memberships |
| Cycle widgets (risk cards, active cycles) | cycles, cycle_issues | issues |
| Module widgets | modules, module_issues | issues |
| Time tracking widgets | time_logs | issues, workspace_memberships |
| Health and risk widgets | issues | activity_log, cycles |
| Workspace summary | projects, cycles, workspace_memberships, pages, modules, issues | None |
| User-specific (welcome, recent) | activity_log, Redis recent-items cache | issues |
Workspace-wide aggregation
By default, dashboard widgets aggregate data across the entire workspace. This means:
- All projects the user has access to are included.
- Guest users see only data from projects they are invited to.
- Archived projects are excluded unless explicitly included in widget configuration.
When a widget is scoped to a specific project or cycle, the aggregation pipeline adds a filter stage that limits results to that scope.
Refresh behavior
Automatic refresh on page load
Every time a user opens a dashboard, all visible widgets fetch fresh data. If cached data is available and within the TTL window, the cache is used.
Manual refresh
Click the Refresh icon in the dashboard toolbar to force all widgets to fetch new data, bypassing the cache.
| Action | Cache behavior |
|---|---|
| Page load | Uses cache if available |
| Manual refresh | Bypasses cache, fetches fresh data |
| Auto-refresh interval | Bypasses cache on each tick |
| Widget configuration change | Fetches fresh data for that widget |
Auto-refresh intervals
Configure an automatic refresh interval for dashboards that stay open on a monitor:
| Interval | Use case |
|---|---|
| Off | Default. Data refreshes only on page load or manual refresh |
| 1 minute | High-frequency monitoring (standup screens) |
| 5 minutes | Active monitoring during sprints |
| 15 minutes | Background awareness |
| 30 minutes | Low-priority overview screens |
Set the interval from the dashboard toolbar ... menu > Auto-refresh.
TIP
Auto-refresh increases API load proportionally. A dashboard with 10 widgets refreshing every minute generates 10 API calls per minute. Use longer intervals for dashboards with many widgets.
Caching strategy
Redis cache structure
Dashboard data is cached in Redis with the following key pattern:
setget:dashboard:{workspace_id}:{widget_type}:{config_hash}| Component | Description |
|---|---|
workspace_id | The workspace this data belongs to |
widget_type | The widget type identifier |
config_hash | SHA-256 hash of the widget's filter and scope configuration |
This ensures that two widgets of the same type with different configurations maintain separate caches.
Cache TTL
| Data type | Default TTL | Rationale |
|---|---|---|
| Issue counts and distributions | 5 minutes | Balances freshness with query cost |
| Activity data (heatmap, timeline) | 10 minutes | Activity patterns change slowly |
| Cycle and module progress | 5 minutes | Sprint data should be reasonably current |
| Health scores | 15 minutes | Composite scores need less frequent updates |
| Workspace summary counts | 10 minutes | Document counts are expensive on large workspaces |
| User-specific data (welcome, recent) | 2 minutes | Personal data should feel fresh |
Cache invalidation
The cache is invalidated automatically in these scenarios:
| Trigger | Scope |
|---|---|
| Issue state change | Widgets filtered to that project |
| Issue created or deleted | All issue-based widgets in the workspace |
| Cycle started or ended | Cycle-related widgets |
| Manual refresh button clicked | All widgets on the current dashboard |
| Widget configuration changed | That specific widget |
Real-time vs. cached data
SetGet dashboards prioritize reliability over real-time precision:
| Aspect | Behavior |
|---|---|
| Data currency | Widgets show data that is at most TTL-old (typically 5-15 minutes) |
| Consistency | All widgets on a dashboard may show data from slightly different points in time |
| Real-time updates | Not pushed to dashboards via WebSocket (unlike notifications) |
| Timer widget | The time tracking timer is real-time via Redis, but time log aggregations are cached |
WARNING
If two team members look at the same dashboard at slightly different times, they may see different numbers. This is expected and resolves on the next refresh cycle.
Performance considerations
Large workspaces
Workspaces with more than 50,000 issues may experience slower aggregation queries. Mitigation strategies:
| Strategy | Description |
|---|---|
| Scope widgets to projects | Reduces the dataset for each query |
| Use longer refresh intervals | Reduces query frequency |
| Limit list widgets | Set lower max-items on Issues Due Soon, Hot Issues, Stalled Issues |
| Avoid many custom query widgets | Custom queries bypass pre-optimized aggregation pipelines |
MongoDB indexes
Dashboard aggregation queries rely on these indexes for performance:
| Collection | Index | Used by |
|---|---|---|
issues | { workspace_id: 1, state_id: 1 } | State distribution, stats bar |
issues | { workspace_id: 1, priority: 1 } | Priority distribution |
issues | { workspace_id: 1, assignee_id: 1 } | Assignee distribution, workload |
issues | { workspace_id: 1, due_date: 1 } | Issues due soon, deadline calendar |
activity_log | { workspace_id: 1, created_at: -1 } | Activity timeline, heatmap |
time_logs | { workspace_id: 1, logged_date: 1 } | Time tracking widgets |
cycle_issues | { cycle_id: 1 } | Cycle progress, risk cards |
module_issues | { module_id: 1 } | Module progress |
API rate limiting
Dashboard API endpoints are rate-limited to prevent abuse:
| Endpoint pattern | Rate limit |
|---|---|
GET /api/workspaces/{slug}/dashboards/{id}/widgets | 60 requests per minute |
GET /api/workspaces/{slug}/dashboards/{id}/widgets/{widget_id}/data | 120 requests per minute |
POST /api/workspaces/{slug}/dashboards/{id}/refresh | 10 requests per minute |
API endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/workspaces/{slug}/dashboards/ | List all dashboards |
POST | /api/workspaces/{slug}/dashboards/ | Create a dashboard |
GET | /api/workspaces/{slug}/dashboards/{id}/ | Get dashboard details |
PATCH | /api/workspaces/{slug}/dashboards/{id}/ | Update dashboard settings |
DELETE | /api/workspaces/{slug}/dashboards/{id}/ | Delete a dashboard |
GET | /api/workspaces/{slug}/dashboards/{id}/widgets/ | List widgets on a dashboard |
POST | /api/workspaces/{slug}/dashboards/{id}/widgets/ | Add a widget |
PATCH | /api/workspaces/{slug}/dashboards/{id}/widgets/{widget_id}/ | Update widget configuration |
DELETE | /api/workspaces/{slug}/dashboards/{id}/widgets/{widget_id}/ | Remove a widget |
GET | /api/workspaces/{slug}/dashboards/{id}/widgets/{widget_id}/data/ | Fetch widget data |
POST | /api/workspaces/{slug}/dashboards/{id}/refresh/ | Force refresh all widgets |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Widget shows stale data | Cache TTL has not expired | Click the manual Refresh button |
| Widget shows "No data" | Scope filter excludes all results | Check widget configuration scope |
| Widget loads slowly | Large dataset without project scope | Scope the widget to a specific project |
| Dashboard takes long to load | Too many widgets fetching simultaneously | Reduce widget count or increase refresh interval |
| Numbers differ between widgets | Widgets cached at different times | Refresh the dashboard to align cache timestamps |