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.
forge/index/refresh.go +23
| @@ -10,6 +10,7 @@ package index | |||
| 10 | 10 | import ( | |
| 11 | 11 | "strings" | |
| 12 | 12 | ||
| 13 | "github.com/rodent-software/vole/core/git" | ||
| 13 | 14 | "github.com/rodent-software/vole/forge/repo" | |
| 14 | 15 | "github.com/rodent-software/vole/metadata" | |
| 15 | 16 | ) | |
| @@ -84,6 +85,15 @@ func (s *Store) IndexRepo(m *repo.Meta) error { | |||
| 84 | 85 | if err != nil { | |
| 85 | 86 | return err | |
| 86 | 87 | } | |
| 88 | // State changes are the metadata commits' subjects, so a rebuild reads the | ||
| 89 | // whole branch and takes every one of them. A repository that never pushed | ||
| 90 | // metadata has no branch to read, which is not an error. | ||
| 91 | var stateCommits []git.Commit | ||
| 92 | if commit != "" { | ||
| 93 | if stateCommits, err = g.Log(metadata.Ref, 0); err != nil { | ||
| 94 | return err | ||
| 95 | } | ||
| 96 | } | ||
| 87 | 97 | ||
| 88 | 98 | tx, err := s.db.Begin() | |
| 89 | 99 | if err != nil { | |
| @@ -121,6 +131,9 @@ func (s *Store) IndexRepo(m *repo.Meta) error { | |||
| 121 | 131 | if err := replaceArtifacts(tx, m.ID, artifacts); err != nil { | |
| 122 | 132 | return err | |
| 123 | 133 | } | |
| 134 | if err := insertStateChanges(tx, m.ID, stateCommits); err != nil { | ||
| 135 | return err | ||
| 136 | } | ||
| 124 | 137 | if _, err := tx.Exec(`UPDATE repos SET issues_sha=? WHERE id=?`, commit, m.ID); err != nil { | |
| 125 | 138 | return err | |
| 126 | 139 | } | |
| @@ -187,6 +200,12 @@ func (s *Store) indexFrom(m *repo.Meta, from, to string) error { | |||
| 187 | 200 | // Numbers are historical: a deleted entity keeps its number, and a creation | |
| 188 | 201 | // that was deleted again inside the range still consumed one. The additions | |
| 189 | 202 | // are therefore taken from the commit range, not from the current tree. | |
| 203 | // The commits in the range carry the state changes: a closed issue is a | ||
| 204 | // subject, not a file to diff. | ||
| 205 | stateCommits, err := g.CommitsBetween(from, to, 0) | ||
| 206 | if err != nil { | ||
| 207 | return err | ||
| 208 | } | ||
| 190 | 209 | groups, err := g.AddedPathsPerCommit(from+".."+to, metadata.IssuesDir, metadata.RequestsDir) | |
| 191 | 210 | if err != nil { | |
| 192 | 211 | return err | |
| @@ -240,6 +259,9 @@ func (s *Store) indexFrom(m *repo.Meta, from, to string) error { | |||
| 240 | 259 | return err | |
| 241 | 260 | } | |
| 242 | 261 | } | |
| 262 | if err := insertStateChanges(tx, m.ID, stateCommits); err != nil { | ||
| 263 | return err | ||
| 264 | } | ||
| 243 | 265 | if err := addNumbers(tx, m.ID, metadata.Additions(groups)); err != nil { | |
| 244 | 266 | return err | |
| 245 | 267 | } | |
| @@ -262,6 +284,7 @@ func deleteRepoRows(tx execer, repoID string) error { | |||
| 262 | 284 | `DELETE FROM request_approvals WHERE repo_id=?`, | |
| 263 | 285 | `DELETE FROM comments WHERE repo_id=?`, | |
| 264 | 286 | `DELETE FROM artifacts WHERE repo_id=?`, | |
| 287 | `DELETE FROM state_changes WHERE repo_id=?`, | ||
| 265 | 288 | `DELETE FROM entity_numbers WHERE repo_id=?`, | |
| 266 | 289 | } { | |
| 267 | 290 | if _, err := tx.Exec(q, repoID); err != nil { | |