| 176 | } |
| 177 | |
| 178 | func (c *RepoAccessCache) getRepoAccessInfo(ctx context.Context, username, owner, repo string) (RepoAccessInfo, error) { |
| 179 | if c == nil { |
| 180 | return RepoAccessInfo{}, fmt.Errorf("nil repo access cache") |
| 181 | } |
| 182 | |
| 183 | key := cacheKey(owner, repo) |
| 184 | userKey := strings.ToLower(username) |
| 185 | |
| 186 | // Entries are immutable once added: the cache table is shared across instances, |
| 187 | // so we publish a fresh entry with a cloned knownUsers map on every miss. |
| 188 | if cacheItem, err := c.cache.Value(key); err == nil { |
| 189 | entry := cacheItem.Data().(*repoAccessCacheEntry) |
| 190 | if cachedHasPush, known := entry.knownUsers[userKey]; known { |
| 191 | c.logDebug(ctx, fmt.Sprintf("repo access cache hit for user %s to %s/%s", username, owner, repo)) |
| 192 | return RepoAccessInfo{ |
| 193 | IsPrivate: entry.isPrivate, |
| 194 | HasPushAccess: cachedHasPush, |
| 195 | }, nil |
| 196 | } |
| 197 | |
| 198 | c.logDebug(ctx, "known users cache miss, fetching permission") |
| 199 | |
| 200 | hasPush, pushErr := c.checkPushAccess(ctx, username, owner, repo) |
| 201 | if pushErr != nil { |
| 202 | return RepoAccessInfo{}, pushErr |
| 203 | } |
| 204 | |
| 205 | users := make(map[string]bool, len(entry.knownUsers)+1) |
| 206 | maps.Copy(users, entry.knownUsers) |
| 207 | users[userKey] = hasPush |
| 208 | c.cache.Add(key, c.ttl, &repoAccessCacheEntry{ |
| 209 | isPrivate: entry.isPrivate, |
| 210 | knownUsers: users, |
| 211 | }) |
| 212 | |
| 213 | return RepoAccessInfo{ |
| 214 | IsPrivate: entry.isPrivate, |
| 215 | HasPushAccess: hasPush, |
| 216 | }, nil |
| 217 | } |
| 218 | |
| 219 | c.logDebug(ctx, fmt.Sprintf("repo access cache miss for user %s to %s/%s", username, owner, repo)) |
| 220 | |
| 221 | isPrivate, viewerLogin, queryErr := c.queryRepoAccessInfo(ctx, owner, repo) |
| 222 | if queryErr != nil { |
| 223 | return RepoAccessInfo{}, queryErr |
| 224 | } |
| 225 | c.setViewerLogin(viewerLogin) |
| 226 | |
| 227 | hasPush, pushErr := c.checkPushAccess(ctx, username, owner, repo) |
| 228 | if pushErr != nil { |
| 229 | return RepoAccessInfo{}, pushErr |
| 230 | } |
| 231 | |
| 232 | c.cache.Add(key, c.ttl, &repoAccessCacheEntry{ |
| 233 | knownUsers: map[string]bool{userKey: hasPush}, |
| 234 | isPrivate: isPrivate, |
| 235 | }) |