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.

Apply a push's index delta as the push lands, and check the ref by reading it

dced06da1bfc4a8e542e5d86d67888362077ed39 · Keenan Nemetz · 10d ago · parent 26c9be33

Apply a push's index delta as the push lands, and check the ref by reading it

A push's metadata delta used to be applied by the next reader, so the
first page after a big push paid for the whole push. And search checked
every visible repository before querying: a `symbolic-ref` to open it and
a `rev-parse` to see whether the metadata branch moved, so N repositories
cost 2N git processes per search.

- Post-receive now opens the index (the SSH server hands it `VOLE_INDEX_DB`
  and the repository id) and runs Ensure after the refs have moved, which
  is also after a followed request's head has moved. A failure is reported
  and ignored: the refs have landed, and the next read still repairs it.
- `git.Repo.RefCommit` resolves a branch ref by reading the loose ref file
  git writes for every branch update, and falls back to `rev-parse` for a
  packed ref, a symbolic ref, or a missing one. The index's Ensure and the
  web's metadata-head read use it.
- `repo.Store.Meta` builds a repository handle without a HEAD lookup, and
  search uses it: the refresh needs the directory and the ACL, not the
  default branch. The per-repository check is now two file reads instead
  of two processes.

Load harness, `search-find` at concurrency 4 over three repositories:
510.2 req/s and p50 7 ms before, 814.8 req/s and p50 4 ms after. A push
that changes the `vole` branch leaves the rows current before the push
returns; a test queries them with no Ensure in between.

forge/repo/repo.go +16−6

@@ -142,6 +142,21 @@ func (s *Store) Public(name string) bool {
142 142
143 143 // Open loads a repo by name, returning an error if it is not a Vole repo.
144 144 func (s *Store) Open(name string) (*Meta, error) {
145 m, err := s.Meta(name)
146 if err != nil {
147 return nil, err
148 }
149 if br := git.Open(m.Dir).Symref(); br != "" {
150 m.DefaultBranch = br
151 }
152 return m, nil
153 }
154
155 // Meta loads a repo's instance-side facts without running git. The id, name,
156 // directory, description and creation time are files and a stat; the default
157 // branch is what Open adds, and that costs a git call. A caller that only needs
158 // the ACL or the git directory should use this.
159 func (s *Store) Meta(name string) (*Meta, error) {
145 160 if err := ValidateName(name); err != nil {
146 161 return nil, err
147 162 }
@@ -155,15 +170,10 @@ func (s *Store) Open(name string) (*Meta, error) {
155 170 return nil, fmt.Errorf("repo %q is not a Vole repository (no %s)", name, idFile)
156 171 }
157 172 m := &Meta{ID: strings.TrimSpace(string(idb)), Name: name, Dir: dir, DefaultBranch: "main", ACLs: s.ACLs}
158 if st, err := os.Stat(dir); err == nil {
159 m.Created = st.ModTime().UTC()
160 }
173 m.Created = st.ModTime().UTC()
161 174 if b, err := os.ReadFile(filepath.Join(dir, descFile)); err == nil {
162 175 m.Description = strings.TrimSpace(string(b))
163 176 }
164 if br := git.Open(dir).Symref(); br != "" {
165 m.DefaultBranch = br
166 }
167 177 return m, nil
168 178 }
169 179