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.

Drive the home rail from the events table

20442b5948ea72c5e84b6cf2ff922f3cc987ac13 · Keenan Nemetz · 9d ago · parent bca9ec24

Drive the home rail from the events table

The landing page still built every visible repository's whole feed in
memory to show the newest ten events across the instance. It now asks the
events table for one bounded page, with the repository names attached, so
the rail costs a page instead of the history.

The cards are separated from the feed:

- counts come from the index (issue and change totals);
- the newest event time per card is one grouped query (`LastEventTimes`)
  instead of deriving it from the feed that was built anyway;
- the release chip comes from the newest release event (`LastReleases`)
  instead of listing tags from git per card;
- the head commit and language still come from git, which is the
  remaining per-repository work on the page.

`visibleRepos` now builds handles with `Meta` rather than `Open`, which
removes a `symbolic-ref` per repository from the landing page and the
instance activity page. The all-git card builder stays as the fallback
for a Web without an index.

Load harness at concurrency 4 with three repositories: home goes from 178
to 243 req/s, p50 from 21 ms to 16 ms.

forge/index/events.go +63

@@ -18,6 +18,7 @@ import (
18 18 "database/sql"
19 19 "fmt"
20 20 "strings"
21 "time"
21 22
22 23 "github.com/rodent-software/vole/core/git"
23 24 "github.com/rodent-software/vole/forge/repo"
@@ -367,3 +368,65 @@ func questionMarks(n int) string {
367 368 }
368 369 return strings.TrimSuffix(strings.Repeat("?,", n), ",")
369 370 }
371
372 // LastEventTimes returns the newest event time per repository. It is the card's
373 // "updated" time, read for a whole page of cards in one grouped query rather
374 // than one query per card.
375 func (s *Store) LastEventTimes(repoIDs []string) (map[string]time.Time, error) {
376 if len(repoIDs) == 0 {
377 return nil, nil
378 }
379 args := make([]any, 0, len(repoIDs))
380 for _, id := range repoIDs {
381 args = append(args, id)
382 }
383 rows, err := s.db.Query(
384 `SELECT repo_id, MAX(at) FROM events WHERE repo_id IN (`+questionMarks(len(repoIDs))+`)
385 GROUP BY repo_id`, args...)
386 if err != nil {
387 return nil, err
388 }
389 defer rows.Close()
390 out := map[string]time.Time{}
391 for rows.Next() {
392 var id string
393 var at int64
394 if err := rows.Scan(&id, &at); err != nil {
395 return nil, err
396 }
397 out[id] = time.Unix(at, 0).UTC()
398 }
399 return out, rows.Err()
400 }
401
402 // LastReleases returns the newest release event's tag name per repository, for a
403 // card's release chip. It reads the same rows the feed does rather than listing
404 // tags from git again.
405 func (s *Store) LastReleases(repoIDs []string) (map[string]string, error) {
406 if len(repoIDs) == 0 {
407 return nil, nil
408 }
409 args := make([]any, 0, len(repoIDs))
410 for _, id := range repoIDs {
411 args = append(args, id)
412 }
413 rows, err := s.db.Query(
414 `SELECT repo_id, title FROM events
415 WHERE kind='release' AND repo_id IN (`+questionMarks(len(repoIDs))+`)
416 ORDER BY at DESC, id DESC`, args...)
417 if err != nil {
418 return nil, err
419 }
420 defer rows.Close()
421 out := map[string]string{}
422 for rows.Next() {
423 var repoID, name string
424 if err := rows.Scan(&repoID, &name); err != nil {
425 return nil, err
426 }
427 if _, ok := out[repoID]; !ok {
428 out[repoID] = name
429 }
430 }
431 return out, rows.Err()
432 }