@@ -25,26 +25,34 @@ type cacheEntry struct {
2525// checkCache checks the cache for a PR and returns the cached data if valid.
2626// Returns (cachedData, cacheHit, hasRunningTests).
2727func (app * App ) checkCache (cacheFile , url string , updatedAt time.Time ) (cachedData * turn.CheckResponse , cacheHit bool , hasRunningTests bool ) {
28- fileData , readErr := os .ReadFile (cacheFile )
29- if readErr != nil {
30- if ! os .IsNotExist (readErr ) {
31- slog .Debug ("[CACHE] Cache file read error" , "url" , url , "error" , readErr )
28+ fileData , err := os .ReadFile (cacheFile )
29+ if err != nil {
30+ if ! os .IsNotExist (err ) {
31+ slog .Debug ("[CACHE] Cache file read error" , "url" , url , "error" , err )
3232 }
3333 return nil , false , false
3434 }
3535
3636 var entry cacheEntry
37- if unmarshalErr := json .Unmarshal (fileData , & entry ); unmarshalErr != nil {
38- slog .Warn ("Failed to unmarshal cache data" , "url" , url , "error" , unmarshalErr )
37+ if err := json .Unmarshal (fileData , & entry ); err != nil {
38+ slog .Warn ("Failed to unmarshal cache data" , "url" , url , "error" , err )
3939 // Remove corrupted cache file
40- if removeErr := os .Remove (cacheFile ); removeErr != nil {
41- slog .Error ("Failed to remove corrupted cache file" , "error" , removeErr )
40+ if err := os .Remove (cacheFile ); err != nil {
41+ slog .Error ("Failed to remove corrupted cache file" , "error" , err )
4242 }
4343 return nil , false , false
4444 }
4545
46+ // Determine TTL based on test state - use shorter TTL for incomplete tests
47+ testState := entry .Data .PullRequest .TestState
48+ isTestIncomplete := testState == "running" || testState == "queued" || testState == "pending"
49+ ttl := cacheTTL
50+ if isTestIncomplete {
51+ ttl = runningTestsCacheTTL
52+ }
53+
4654 // Check if cache is expired or PR updated
47- if time .Since (entry .CachedAt ) >= cacheTTL || ! entry .UpdatedAt .Equal (updatedAt ) {
55+ if time .Since (entry .CachedAt ) >= ttl || ! entry .UpdatedAt .Equal (updatedAt ) {
4856 // Log why cache was invalid
4957 if ! entry .UpdatedAt .Equal (updatedAt ) {
5058 slog .Debug ("[CACHE] Cache miss - PR updated" ,
@@ -56,15 +64,14 @@ func (app *App) checkCache(cacheFile, url string, updatedAt time.Time) (cachedDa
5664 "url" , url ,
5765 "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
5866 "cache_age" , time .Since (entry .CachedAt ).Round (time .Second ),
59- "ttl" , cacheTTL )
67+ "ttl" , ttl ,
68+ "test_state" , testState )
6069 }
61- return nil , false , false
70+ return nil , false , isTestIncomplete
6271 }
6372
64- // Check for incomplete tests that should invalidate cache
73+ // Check for incomplete tests that should invalidate cache and trigger Turn API cache bypass
6574 cacheAge := time .Since (entry .CachedAt )
66- testState := entry .Data .PullRequest .TestState
67- isTestIncomplete := testState == "running" || testState == "queued" || testState == "pending"
6875 if entry .Data != nil && isTestIncomplete && cacheAge < runningTestsCacheBypass {
6976 slog .Debug ("[CACHE] Cache invalidated - tests incomplete and cache entry is fresh" ,
7077 "url" , url ,
@@ -144,15 +151,15 @@ func (app *App) turnData(ctx context.Context, url string, updatedAt time.Time) (
144151 "timestamp_sent" , timestampToSend .Format (time .RFC3339 ))
145152 }
146153
147- var retryErr error
154+ var err error
148155 slog .Debug ("[TURN] Making API call" ,
149156 "url" , url ,
150157 "user" , app .currentUser .GetLogin (),
151158 "pr_updated_at" , timestampToSend .Format (time .RFC3339 ))
152- data , retryErr = app .turnClient .Check (turnCtx , url , app .currentUser .GetLogin (), timestampToSend )
153- if retryErr != nil {
154- slog .Warn ("Turn API error (will retry)" , "error" , retryErr )
155- return retryErr
159+ data , err = app .turnClient .Check (turnCtx , url , app .currentUser .GetLogin (), timestampToSend )
160+ if err != nil {
161+ slog .Warn ("Turn API error (will retry)" , "error" , err )
162+ return err
156163 }
157164 slog .Debug ("[TURN] API call successful" , "url" , url )
158165 return nil
@@ -188,45 +195,36 @@ func (app *App) turnData(ctx context.Context, url string, updatedAt time.Time) (
188195 }
189196
190197 // Save to cache (don't fail if caching fails) - skip if --no-cache is set
191- // Don't cache when tests are incomplete - always re-poll to catch completion
192- if ! app .noCache {
193- shouldCache := true
194-
195- // Never cache PRs with incomplete tests - we want fresh data on every poll
196- testState := ""
197- if data != nil {
198- testState = data .PullRequest .TestState
199- }
198+ // Cache PRs with incomplete tests using short TTL to catch completion quickly
199+ if ! app .noCache && data != nil {
200+ testState := data .PullRequest .TestState
200201 isTestIncomplete := testState == "running" || testState == "queued" || testState == "pending"
201- if data != nil && isTestIncomplete {
202- shouldCache = false
203- slog .Debug ("[CACHE] Skipping cache for PR with incomplete tests" ,
204- "url" , url ,
205- "test_state" , testState ,
206- "pending_checks" , len (data .PullRequest .CheckSummary .Pending ))
207- }
208202
209- if shouldCache {
210- entry := cacheEntry {
211- Data : data ,
212- CachedAt : time .Now (),
213- UpdatedAt : updatedAt ,
214- }
215- if cacheData , marshalErr := json .Marshal (entry ); marshalErr != nil {
216- slog .Error ("Failed to marshal cache data" , "url" , url , "error" , marshalErr )
203+ entry := cacheEntry {
204+ Data : data ,
205+ CachedAt : time .Now (),
206+ UpdatedAt : updatedAt ,
207+ }
208+ if cacheData , err := json .Marshal (entry ); err != nil {
209+ slog .Error ("Failed to marshal cache data" , "url" , url , "error" , err )
210+ } else {
211+ // Ensure cache directory exists with secure permissions
212+ if err := os .MkdirAll (filepath .Dir (cacheFile ), 0o700 ); err != nil {
213+ slog .Error ("Failed to create cache directory" , "error" , err )
214+ } else if err := os .WriteFile (cacheFile , cacheData , 0o600 ); err != nil {
215+ slog .Error ("Failed to write cache file" , "error" , err )
217216 } else {
218- // Ensure cache directory exists with secure permissions
219- if dirErr := os .MkdirAll (filepath .Dir (cacheFile ), 0o700 ); dirErr != nil {
220- slog .Error ("Failed to create cache directory" , "error" , dirErr )
221- } else if writeErr := os .WriteFile (cacheFile , cacheData , 0o600 ); writeErr != nil {
222- slog .Error ("Failed to write cache file" , "error" , writeErr )
223- } else {
224- slog .Debug ("[CACHE] Saved to cache" ,
225- "url" , url ,
226- "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
227- "pr_updated_at" , entry .UpdatedAt .Format (time .RFC3339 ),
228- "cache_file" , filepath .Base (cacheFile ))
217+ ttl := cacheTTL
218+ if isTestIncomplete {
219+ ttl = runningTestsCacheTTL
229220 }
221+ slog .Debug ("[CACHE] Saved to cache" ,
222+ "url" , url ,
223+ "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
224+ "pr_updated_at" , entry .UpdatedAt .Format (time .RFC3339 ),
225+ "ttl" , ttl ,
226+ "test_state" , testState ,
227+ "cache_file" , filepath .Base (cacheFile ))
230228 }
231229 }
232230 }
@@ -258,8 +256,8 @@ func (app *App) cleanupOldCache() {
258256 // Remove cache files older than cleanup interval (15 days)
259257 if time .Since (info .ModTime ()) > cacheCleanupInterval {
260258 filePath := filepath .Join (app .cacheDir , entry .Name ())
261- if removeErr := os .Remove (filePath ); removeErr != nil {
262- slog .Error ("Failed to remove old cache file" , "file" , filePath , "error" , removeErr )
259+ if err := os .Remove (filePath ); err != nil {
260+ slog .Error ("Failed to remove old cache file" , "file" , filePath , "error" , err )
263261 errorCount ++
264262 } else {
265263 cleanupCount ++
0 commit comments