vole
Clone
Read only, no credentials:
https://rodent.software/git/vole.git
Write over SSH with a registered key:
git clone ssh://git@0.0.0.0:2222/vole.git
A free and open-source forge that lives in your Git repo.
Index state changes for the activity feed
4d938fe894d56ff29b87fa0f2ebec50a744f2993 · Keenan Nemetz · 9d ago · parent e2a58f3a
Index state changes for the activity feed The activity feed found closed, reopened and merged events by reading the last 200 metadata commit subjects on every render: one git process per repository, so the instance home with N repositories ran N of them. It was the last derived read still going to git. - A `state_changes` table is fed by the same refresh: a delta reads the commits in its range, a rebuild reads the whole metadata branch. Rows are keyed by the commit, so a retried or overlapping refresh cannot duplicate an event, and an ordinal keeps two changes in one second in branch order (commit times are seconds; a close and a reopen often share one). - `Store.StateChanges` returns the events newest first; the feed reads it and keeps the metadata log as the fallback for a server without an index or a failed read. - `git.Repo.Log` and `CommitsBetween` treat a limit of zero as no limit, which the rebuild needs, and `metadata.ParseStateSubject` is exported so the index derives the events without a second copy of the format. The activity page now starts no metadata log; the instance home and the instance activity page lose one process per visible repository. On the load harness with three repositories: activity 496 req/s (p50 7 ms), instance activity 230 req/s (p50 17 ms), home 202 req/s (p50 20 ms). Schema version 7 rebuilds each repository on its first read after upgrade.
server/web/activity.go +20−1
| @@ -88,7 +88,7 @@ func (w *Web) repoActivity(memo *requestMemo, m *repo.Meta, kind, actor, assigne | |||
| 88 | 88 | ||
| 89 | 89 | var stateChanges []metadata.StateChange | |
| 90 | 90 | if kind == "" || kind == "issue" || kind == "change" { | |
| 91 | stateChanges, _ = metadata.LoadStateChanges(g, 200) | ||
| 91 | stateChanges = w.stateChanges(memo, m) | ||
| 92 | 92 | } | |
| 93 | 93 | issuesByID := map[string]*activityIssue{} | |
| 94 | 94 | requestsByID := map[string]*activityRequest{} | |
| @@ -366,6 +366,25 @@ func (w *Web) activityRequests(memo *requestMemo, m *repo.Meta) []activityReques | |||
| 366 | 366 | return out | |
| 367 | 367 | } | |
| 368 | 368 | ||
| 369 | // activityHistory is how far back the feed looks for state changes. The feed | ||
| 370 | // itself keeps fewer events; this is the window a closed issue has to be seen in. | ||
| 371 | const activityHistory = 200 | ||
| 372 | |||
| 373 | // stateChanges reads closed, reopened and merged events from the index for a read | ||
| 374 | // request. A Web without an index, or a failed read, falls back to the metadata | ||
| 375 | // log, which is where the events come from either way. | ||
| 376 | func (w *Web) stateChanges(memo *requestMemo, m *repo.Meta) []metadata.StateChange { | ||
| 377 | if w.Index != nil { | ||
| 378 | if err := w.ensureIndex(memo, m); err == nil { | ||
| 379 | if list, err := w.Index.StateChanges(m.ID, activityHistory); err == nil { | ||
| 380 | return list | ||
| 381 | } | ||
| 382 | } | ||
| 383 | } | ||
| 384 | list, _ := metadata.LoadStateChanges(m.Git(), activityHistory) | ||
| 385 | return list | ||
| 386 | } | ||
| 387 | |||
| 369 | 388 | func activityAssignees(events []activityEvent) []string { | |
| 370 | 389 | seen := map[string]bool{} | |
| 371 | 390 | var out []string | |