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/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Read what a page needs once per request

5d440e37f16d06790ce87e2f8b7dcc7d9bf0532c · Keenan Nemetz · 10d ago · parent 34cbc99f

Read what a page needs once per request

A page and its header are built from the same facts, and each of them was
read separately: the repository (twice, each resolving HEAD's branch with
a git call), the open change requests (once for the header's count, once
for the page), and a change request looked up by reading every request.
On a repository with 30 change requests the overview paid 21 git
invocations after the metadata batching, and the requests list still paid
8.

Give each read request one memo, installed by the middleware, and have the
handlers and the header read through it. It lasts one request and covers
reads only: a request that writes metadata gets no memo, so there is no
TTL, no cross-request cache, and no invalidation protocol, and a push is
still visible on the next page load.

The header now takes the page it is rendering (`nav .Page "issues"`)
instead of six fields the view already carries, which is also why the memo
can reach it at all. Templates are unchanged in what they output.

server/web/home.go +9−9

@@ -97,7 +97,7 @@ func (w *Web) IndexPage(rw http.ResponseWriter, r *http.Request) {
97 97 }
98 98
99 99 var events []activityEvent
100 data.Cards, events = w.repoCards(entries)
100 data.Cards, events = w.repoCards(memoOf(r), entries)
101 101 if query != "" {
102 102 sort.SliceStable(data.Cards, func(i, j int) bool { return data.Cards[i].Name < data.Cards[j].Name })
103 103 } else {
@@ -121,7 +121,7 @@ func (w *Web) InstanceActivity(rw http.ResponseWriter, r *http.Request) {
121 121 return
122 122 }
123 123 number, _ := strconv.Atoi(r.URL.Query().Get("page"))
124 events, pager := paginate(w.instanceEvents(entries, kind), kind, number, instanceEventPage)
124 events, pager := paginate(w.instanceEvents(memoOf(r), entries, kind), kind, number, instanceEventPage)
125 125 w.render(rw, "instance_activity", instanceActivityData{
126 126 Page: w.page(r, "Activity"), Kind: kind, Events: events, Pager: pager,
127 127 }, 0)
@@ -138,7 +138,7 @@ func (w *Web) visibleRepos(r *http.Request, query string) ([]index.Entry, error)
138 138 needle := strings.ToLower(strings.TrimSpace(query))
139 139 var visible []index.Entry
140 140 for _, e := range entries {
141 m, err := w.Repos.Open(e.Name)
141 m, err := w.repo(memoOf(r), e.Name)
142 142 if err != nil {
143 143 continue
144 144 }
@@ -158,15 +158,15 @@ func (w *Web) visibleRepos(r *http.Request, query string) ([]index.Entry, error)
158 158 // repoCards reads each repository once and returns its card plus its events with
159 159 // the repository named on them. It replaces the separate card and feed passes so
160 160 // the landing page does not walk each repository twice.
161 func (w *Web) repoCards(entries []index.Entry) ([]repoCard, []activityEvent) {
161 func (w *Web) repoCards(memo *requestMemo, entries []index.Entry) ([]repoCard, []activityEvent) {
162 162 cards := make([]repoCard, 0, len(entries))
163 163 var events []activityEvent
164 164 for _, e := range entries {
165 m, err := w.Repos.Open(e.Name)
165 m, err := w.repo(memo, e.Name)
166 166 if err != nil {
167 167 continue
168 168 }
169 feed := w.repoActivity(m, "", "", "")
169 feed := w.repoActivity(memo, m, "", "", "")
170 170 card := repoCard{
171 171 Name: e.Name, Description: e.Description, Branch: m.DefaultBranch,
172 172 Private: true, Commit: feed.LastCommit, LastTag: feed.LastTag,
@@ -201,14 +201,14 @@ func (w *Web) repoCards(entries []index.Entry) ([]repoCard, []activityEvent) {
201 201 // instanceEvents merges the visible repositories' feeds for one kind. It is
202 202 // bounded after the merge: a per-repository cap would hide a quiet repository's
203 203 // only event on an instance with a busy neighbour.
204 func (w *Web) instanceEvents(entries []index.Entry, kind string) []activityEvent {
204 func (w *Web) instanceEvents(memo *requestMemo, entries []index.Entry, kind string) []activityEvent {
205 205 var events []activityEvent
206 206 for _, e := range entries {
207 m, err := w.Repos.Open(e.Name)
207 m, err := w.repo(memo, e.Name)
208 208 if err != nil {
209 209 continue
210 210 }
211 for _, ev := range w.repoActivity(m, kind, "", "").Events {
211 for _, ev := range w.repoActivity(memo, m, kind, "", "").Events {
212 212 ev.Repo = m.Name
213 213 events = append(events, ev)
214 214 }