@@ -62,11 +62,12 @@ Please update your commit message(s) by doing |git commit --amend| and then |git
6262`
6363
6464const (
65- unknown = "Unknown"
66- failureState = "failure"
67- successState = "success"
68- svgVersion = "?v=2"
69- QuickCacheTTL = 5 * time .Minute
65+ unknown = "Unknown"
66+ failureState = "failure"
67+ successState = "success"
68+ svgVersion = "?v=2"
69+ NegativeCacheTTL = 3 * time .Minute // Used for negative caching of missing/not-signed users
70+ ProjectCacheTTL = 3 * time .Hour // Used for per-project caching of signed users
7071)
7172
7273// GraphQL related types
@@ -181,6 +182,15 @@ func (c *Cache) Set(key [2]string, value *github.User) {
181182 }
182183}
183184
185+ func (c * Cache ) SetWithTTL (key [2 ]string , value * github.User , tl time.Duration ) {
186+ c .mu .Lock ()
187+ defer c .mu .Unlock ()
188+ c .data [key ] = cacheEntry {
189+ value : value ,
190+ expiresAt : time .Now ().Add (tl ),
191+ }
192+ }
193+
184194func (c * Cache ) Cleanup () {
185195 c .mu .Lock ()
186196 defer c .mu .Unlock ()
@@ -192,6 +202,12 @@ func (c *Cache) Cleanup() {
192202 }
193203}
194204
205+ func (c * Cache ) Clear () {
206+ c .mu .Lock ()
207+ defer c .mu .Unlock ()
208+ c .data = make (map [[2 ]string ]cacheEntry )
209+ }
210+
195211func (c * Cache ) Delete (key [2 ]string ) { c .mu .Lock (); delete (c .data , key ); c .mu .Unlock () }
196212
197213type userCacheEntry struct {
@@ -254,6 +270,12 @@ func (c *UserCache) Cleanup() {
254270 }
255271}
256272
273+ func (c * UserCache ) Clear () {
274+ c .mu .Lock ()
275+ defer c .mu .Unlock ()
276+ c .data = make (map [[3 ]string ]userCacheEntry )
277+ }
278+
257279func (c * UserCache ) Delete (key [3 ]string ) { c .mu .Lock (); delete (c .data , key ); c .mu .Unlock () }
258280
259281type projectUserCacheEntry struct {
@@ -322,11 +344,17 @@ func (c *ProjectUserCache) Cleanup() {
322344 }
323345}
324346
347+ func (c * ProjectUserCache ) Clear () {
348+ c .mu .Lock ()
349+ defer c .mu .Unlock ()
350+ c .data = make (map [[4 ]string ]projectUserCacheEntry )
351+ }
352+
325353func (c * ProjectUserCache ) Delete (key [4 ]string ) { c .mu .Lock (); delete (c .data , key ); c .mu .Unlock () }
326354
327- var GithubUserCache = NewCache (24 * time .Hour )
328- var ModelUserCache = NewUserCache (24 * time .Hour )
329- var ModelProjectUserCache = NewProjectUserCache (6 * time .Hour )
355+ var GithubUserCache = NewCache (12 * time .Hour )
356+ var ModelUserCache = NewUserCache (12 * time .Hour )
357+ var ModelProjectUserCache = NewProjectUserCache (3 * time .Hour )
330358
331359func init () {
332360 go func () {
@@ -339,6 +367,17 @@ func init() {
339367 }()
340368}
341369
370+ // ClearCaches clears all in-memory caches maintained by the GitHub module.
371+ func ClearCaches () {
372+ f := logrus.Fields {
373+ "functionName" : "github.github_repository.ClearAllCaches" ,
374+ }
375+ GithubUserCache .Clear ()
376+ ModelUserCache .Clear ()
377+ ModelProjectUserCache .Clear ()
378+ log .WithFields (f ).Info ("cleared caches" )
379+ }
380+
342381func GetGitHubRepository (ctx context.Context , installationID , githubRepositoryID int64 ) (* github.Repository , error ) {
343382 f := logrus.Fields {
344383 "functionName" : "github.github_repository.GetGitHubRepository" ,
@@ -799,6 +838,7 @@ func GetCoAuthorCommits(
799838 Authorized : false ,
800839 }
801840 log .WithFields (f ).Debugf ("PR: %d, %+v" , pr , summary )
841+ GithubUserCache .Set (cacheKey , user )
802842 } else {
803843 summary = & UserCommitSummary {
804844 SHA : utils .StringValue (commit .SHA ),
@@ -812,9 +852,9 @@ func GetCoAuthorCommits(
812852 Authorized : false ,
813853 }
814854 log .WithFields (f ).Debugf ("Co-author GitHub user details not found: %v" , coAuthor )
855+ GithubUserCache .SetWithTTL (cacheKey , user , 30 * time .Minute ) // negative cache for 30 minutes
815856 }
816857
817- GithubUserCache .Set (cacheKey , user )
818858 return summary , found
819859}
820860
@@ -973,7 +1013,7 @@ func GetCommitAuthorSignedStatus(
9731013 * unsigned = append (* unsigned , userSummary )
9741014 mu .Unlock ()
9751015 log .WithFields (f ).Debugf ("store per-project cache: unsigned, user is null (%+v)" , projectCacheKey )
976- ModelProjectUserCache .SetWithTTL (projectCacheKey , nil , false , false , QuickCacheTTL )
1016+ ModelProjectUserCache .SetWithTTL (projectCacheKey , nil , false , false , NegativeCacheTTL )
9771017 return
9781018 }
9791019 user := cachedUser
@@ -984,7 +1024,7 @@ func GetCommitAuthorSignedStatus(
9841024 * unsigned = append (* unsigned , userSummary )
9851025 mu .Unlock ()
9861026 log .WithFields (f ).Debugf ("store per-project cache: unsigned, hasUserSigned error (%+v)" , projectCacheKey )
987- ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , false , QuickCacheTTL )
1027+ ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , false , NegativeCacheTTL )
9881028 return
9891029 }
9901030
@@ -1005,14 +1045,14 @@ func GetCommitAuthorSignedStatus(
10051045 * unsigned = append (* unsigned , userSummary )
10061046 mu .Unlock ()
10071047 log .WithFields (f ).Debugf ("store per-project cache: unsigned, authorized is false (%+v)" , projectCacheKey )
1008- ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , QuickCacheTTL )
1048+ ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , NegativeCacheTTL )
10091049 }
10101050 } else {
10111051 mu .Lock ()
10121052 * unsigned = append (* unsigned , userSummary )
10131053 mu .Unlock ()
10141054 log .WithFields (f ).Debugf ("store per-project cache: unsigned, userSigned is null (%+v)" , projectCacheKey )
1015- ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , QuickCacheTTL )
1055+ ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , NegativeCacheTTL )
10161056 }
10171057 return
10181058 }
@@ -1059,8 +1099,8 @@ func GetCommitAuthorSignedStatus(
10591099 mu .Lock ()
10601100 * unsigned = append (* unsigned , userSummary )
10611101 mu .Unlock ()
1062- ModelProjectUserCache .SetWithTTL (projectCacheKey , nil , false , false , QuickCacheTTL )
1063- ModelUserCache .SetWithTTL (cacheKey , nil , QuickCacheTTL )
1102+ ModelProjectUserCache .SetWithTTL (projectCacheKey , nil , false , false , NegativeCacheTTL )
1103+ ModelUserCache .SetWithTTL (cacheKey , nil , NegativeCacheTTL )
10641104 return
10651105 }
10661106
@@ -1072,8 +1112,8 @@ func GetCommitAuthorSignedStatus(
10721112 mu .Lock ()
10731113 * unsigned = append (* unsigned , userSummary )
10741114 mu .Unlock ()
1075- ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , false , QuickCacheTTL )
1076- ModelUserCache .SetWithTTL (cacheKey , user , QuickCacheTTL )
1115+ ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , false , NegativeCacheTTL )
1116+ ModelUserCache .SetWithTTL (cacheKey , user , NegativeCacheTTL )
10771117 return
10781118 }
10791119
@@ -1095,16 +1135,16 @@ func GetCommitAuthorSignedStatus(
10951135 mu .Lock ()
10961136 * unsigned = append (* unsigned , userSummary )
10971137 mu .Unlock ()
1098- ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , QuickCacheTTL )
1099- ModelUserCache .SetWithTTL (cacheKey , user , QuickCacheTTL )
1138+ ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , NegativeCacheTTL )
1139+ ModelUserCache .SetWithTTL (cacheKey , user , NegativeCacheTTL )
11001140 }
11011141 } else {
11021142 log .WithFields (f ).Debugf ("store caches: unsigned, userSigned is null (%+v)" , projectCacheKey )
11031143 mu .Lock ()
11041144 * unsigned = append (* unsigned , userSummary )
11051145 mu .Unlock ()
1106- ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , QuickCacheTTL )
1107- ModelUserCache .SetWithTTL (cacheKey , user , QuickCacheTTL )
1146+ ModelProjectUserCache .SetWithTTL (projectCacheKey , user , false , userSummary .Affiliated , NegativeCacheTTL )
1147+ ModelUserCache .SetWithTTL (cacheKey , user , NegativeCacheTTL )
11081148 }
11091149}
11101150
@@ -1542,6 +1582,79 @@ func hasCheckPreviouslyFailed(ctx context.Context, client *github.Client, owner,
15421582 return false , nil , nil
15431583}
15441584
1585+ // UpdateCacheAfterSignature marks the user as authorized for the given project
1586+ func UpdateCacheAfterSignature (ctx context.Context , user * models.User , projectID string ) error {
1587+ f := logrus.Fields {
1588+ "functionName" : "github.github_repository.UpdateCacheAfterSignature" ,
1589+ "projectID" : projectID ,
1590+ }
1591+
1592+ if user == nil {
1593+ log .WithFields (f ).Warn ("nil user passed to UpdateCacheAfterSignature" )
1594+ return fmt .Errorf ("nil user" )
1595+ }
1596+ projectID = strings .TrimSpace (projectID )
1597+ if projectID == "" {
1598+ log .WithFields (f ).Warn ("empty projectID passed to UpdateCacheAfterSignature" )
1599+ return fmt .Errorf ("empty projectID" )
1600+ }
1601+
1602+ githubID := strings .TrimSpace (user .GithubID )
1603+ githubLogin := strings .TrimSpace (user .GithubUsername )
1604+
1605+ if githubID == "" || githubLogin == "" {
1606+ log .WithFields (f ).Debugf ("user lacks GitHub ID or username - skipping cache update (githubID=%q, login=%q)" , githubID , githubLogin )
1607+ return nil
1608+ }
1609+
1610+ affiliated := strings .TrimSpace (user .CompanyID ) != ""
1611+
1612+ emails := collectUserEmails (user )
1613+ if len (emails ) == 0 {
1614+ log .WithFields (f ).Debugf ("no emails found for user (githubID=%s, login=%s) - nothing to cache" , githubID , githubLogin )
1615+ return nil
1616+ }
1617+
1618+ loginLower := strings .ToLower (githubLogin )
1619+
1620+ for _ , email := range emails {
1621+ genKey := UserKey (githubID , loginLower , email )
1622+ ModelUserCache .Set (genKey , user )
1623+
1624+ projKey := ProjectUserKey (projectID , githubID , loginLower , email )
1625+ ModelProjectUserCache .Set (projKey , user , true , affiliated )
1626+ }
1627+
1628+ log .WithFields (f ).Infof ("updated caches for user login=%q (GitHubID=%s), project=%s: marked as authorized for %d email(s)" ,
1629+ loginLower , githubID , projectID , len (emails ))
1630+
1631+ return nil
1632+ }
1633+
1634+ // collectUserEmails returns a de-duplicated, lowercased list of the user's emails.
1635+ func collectUserEmails (u * models.User ) []string {
1636+ uniq := make (map [string ]struct {}, 4 )
1637+ add := func (s string ) {
1638+ s = strings .ToLower (strings .TrimSpace (s ))
1639+ if s != "" {
1640+ uniq [s ] = struct {}{}
1641+ }
1642+ }
1643+
1644+ add (string (u .LfEmail ))
1645+
1646+ for _ , em := range u .Emails {
1647+ add (em )
1648+ }
1649+
1650+ out := make ([]string , 0 , len (uniq ))
1651+ for e := range uniq {
1652+ out = append (out , e )
1653+ }
1654+ sort .Strings (out )
1655+ return out
1656+ }
1657+
15451658func hasCheckPreviouslySucceeded (ctx context.Context , client * github.Client , owner , repo string , pullRequestID int ) (bool , * github.IssueComment , error ) {
15461659 f := logrus.Fields {
15471660 "functionName" : "github.github_repository.hasCheckPreviouslySucceeded" ,
0 commit comments