Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

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