blob: 1fbc0be806a778dfcfabc8769a061613431536c7 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gerrit
import (
"encoding/json"
"sync"
)
// accountCache is a cache of accounts we read from a Gerrit instance.
// It maintains a mapping from account ID to account information so
// that we can easily compare accounts for equality and so that we don't
// don'get another copy of the account information every time we unmarshal.
type accountCache struct {
mu sync.Mutex
accounts map[int]*AccountInfo
}
// loadAccount unmarshals the JSON for a single Gerrit account.
// It returns a canonical AccountInfo.
// This may be called concurrently by multiple goroutines.
func (c *Client) loadAccount(accountJSON json.RawMessage) *AccountInfo {
if len(accountJSON) == 0 {
return nil
}
var id struct {
AccountID int `json:"_account_id"`
}
if err := json.Unmarshal(accountJSON, &id); err != nil {
c.slog.Error("gerrit account ID decode failure", "data", accountJSON, "err", err)
c.db.Panic("gerrit account ID decode failure: %v", err)
}
ac := &c.ac
ac.mu.Lock()
ai, ok := ac.accounts[id.AccountID]
ac.mu.Unlock()
if ok {
return ai
}
if err := json.Unmarshal(accountJSON, &ai); err != nil {
c.slog.Error("gerrit account decode failure", "num", id.AccountID, "data", accountJSON, "err", err)
c.db.Panic("gerrit account decode failure for %d: %v", id.AccountID, err)
}
// Use a function literal so we can defer the unlock.
update := func() *AccountInfo {
ac.mu.Lock()
defer ac.mu.Unlock()
aiNew, ok := ac.accounts[id.AccountID]
if ok {
// Somebody else already cached this account.
return aiNew
}
if ac.accounts == nil {
ac.accounts = make(map[int]*AccountInfo)
}
ac.accounts[id.AccountID] = ai
return ai
}
return update()
}