@@ -25,6 +25,8 @@ type cacheEntry struct {
2525
2626// turnData fetches Turn API data with caching.
2727func (app * App ) turnData (ctx context.Context , url string , updatedAt time.Time ) (* turn.CheckResponse , bool , error ) {
28+ prAge := time .Since (updatedAt )
29+ hasRunningTests := false
2830 // Validate URL before processing
2931 if err := validateURL (url ); err != nil {
3032 return nil , false , fmt .Errorf ("invalid URL: %w" , err )
@@ -55,15 +57,26 @@ func (app *App) turnData(ctx context.Context, url string, updatedAt time.Time) (
5557 }
5658 } else if time .Since (entry .CachedAt ) < cacheTTL && entry .UpdatedAt .Equal (updatedAt ) {
5759 // Check if cache is still valid (10 day TTL, but PR UpdatedAt is primary check)
58- slog .Debug ("[CACHE] Cache hit" ,
59- "url" , url ,
60- "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
61- "cache_age" , time .Since (entry .CachedAt ).Round (time .Second ),
62- "pr_updated_at" , entry .UpdatedAt .Format (time .RFC3339 ))
63- if app .healthMonitor != nil {
64- app .healthMonitor .recordCacheAccess (true )
60+ // But invalidate cache for PRs with running tests if they're fresh (< 90 minutes old)
61+ if entry .Data != nil && entry .Data .PullRequest .TestState == "running" && prAge < runningTestsCacheBypass {
62+ hasRunningTests = true
63+ slog .Debug ("[CACHE] Cache invalidated - PR has running tests and is fresh" ,
64+ "url" , url ,
65+ "test_state" , entry .Data .PullRequest .TestState ,
66+ "pr_age" , prAge .Round (time .Minute ),
67+ "cached_at" , entry .CachedAt .Format (time .RFC3339 ))
68+ // Don't return cached data - fall through to fetch fresh data with current time
69+ } else {
70+ slog .Debug ("[CACHE] Cache hit" ,
71+ "url" , url ,
72+ "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
73+ "cache_age" , time .Since (entry .CachedAt ).Round (time .Second ),
74+ "pr_updated_at" , entry .UpdatedAt .Format (time .RFC3339 ))
75+ if app .healthMonitor != nil {
76+ app .healthMonitor .recordCacheAccess (true )
77+ }
78+ return entry .Data , true , nil
6579 }
66- return entry .Data , true , nil
6780 } else {
6881 // Log why cache was invalid
6982 if ! entry .UpdatedAt .Equal (updatedAt ) {
@@ -103,12 +116,22 @@ func (app *App) turnData(ctx context.Context, url string, updatedAt time.Time) (
103116 turnCtx , cancel := context .WithTimeout (ctx , turnAPITimeout )
104117 defer cancel ()
105118
119+ // For PRs with running tests, send current time to bypass Turn server cache
120+ timestampToSend := updatedAt
121+ if hasRunningTests {
122+ timestampToSend = time .Now ()
123+ slog .Debug ("[TURN] Using current timestamp for PR with running tests to bypass Turn server cache" ,
124+ "url" , url ,
125+ "pr_updated_at" , updatedAt .Format (time .RFC3339 ),
126+ "timestamp_sent" , timestampToSend .Format (time .RFC3339 ))
127+ }
128+
106129 var retryErr error
107130 slog .Debug ("[TURN] Making API call" ,
108131 "url" , url ,
109132 "user" , app .currentUser .GetLogin (),
110- "pr_updated_at" , updatedAt .Format (time .RFC3339 ))
111- data , retryErr = app .turnClient .Check (turnCtx , url , app .currentUser .GetLogin (), updatedAt )
133+ "pr_updated_at" , timestampToSend .Format (time .RFC3339 ))
134+ data , retryErr = app .turnClient .Check (turnCtx , url , app .currentUser .GetLogin (), timestampToSend )
112135 if retryErr != nil {
113136 slog .Warn ("Turn API error (will retry)" , "error" , retryErr )
114137 return retryErr
@@ -137,26 +160,42 @@ func (app *App) turnData(ctx context.Context, url string, updatedAt time.Time) (
137160 }
138161
139162 // Save to cache (don't fail if caching fails) - skip if --no-cache is set
163+ // Also skip caching if tests are running and PR is fresh (updated in last 90 minutes)
140164 if ! app .noCache {
141- entry := cacheEntry {
142- Data : data ,
143- CachedAt : time .Now (),
144- UpdatedAt : updatedAt ,
165+ shouldCache := true
166+ prAge := time .Since (updatedAt )
167+
168+ // Don't cache PRs with running tests unless they're older than 90 minutes
169+ if data != nil && data .PullRequest .TestState == "running" && prAge < runningTestsCacheBypass {
170+ shouldCache = false
171+ slog .Debug ("[CACHE] Skipping cache for PR with running tests" ,
172+ "url" , url ,
173+ "test_state" , data .PullRequest .TestState ,
174+ "pr_age" , prAge .Round (time .Minute ),
175+ "pending_checks" , len (data .PullRequest .CheckSummary .PendingStatuses ))
145176 }
146- if cacheData , marshalErr := json .Marshal (entry ); marshalErr != nil {
147- slog .Error ("Failed to marshal cache data" , "url" , url , "error" , marshalErr )
148- } else {
149- // Ensure cache directory exists with secure permissions
150- if dirErr := os .MkdirAll (filepath .Dir (cacheFile ), 0o700 ); dirErr != nil {
151- slog .Error ("Failed to create cache directory" , "error" , dirErr )
152- } else if writeErr := os .WriteFile (cacheFile , cacheData , 0o600 ); writeErr != nil {
153- slog .Error ("Failed to write cache file" , "error" , writeErr )
177+
178+ if shouldCache {
179+ entry := cacheEntry {
180+ Data : data ,
181+ CachedAt : time .Now (),
182+ UpdatedAt : updatedAt ,
183+ }
184+ if cacheData , marshalErr := json .Marshal (entry ); marshalErr != nil {
185+ slog .Error ("Failed to marshal cache data" , "url" , url , "error" , marshalErr )
154186 } else {
155- slog .Debug ("[CACHE] Saved to cache" ,
156- "url" , url ,
157- "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
158- "pr_updated_at" , entry .UpdatedAt .Format (time .RFC3339 ),
159- "cache_file" , filepath .Base (cacheFile ))
187+ // Ensure cache directory exists with secure permissions
188+ if dirErr := os .MkdirAll (filepath .Dir (cacheFile ), 0o700 ); dirErr != nil {
189+ slog .Error ("Failed to create cache directory" , "error" , dirErr )
190+ } else if writeErr := os .WriteFile (cacheFile , cacheData , 0o600 ); writeErr != nil {
191+ slog .Error ("Failed to write cache file" , "error" , writeErr )
192+ } else {
193+ slog .Debug ("[CACHE] Saved to cache" ,
194+ "url" , url ,
195+ "cached_at" , entry .CachedAt .Format (time .RFC3339 ),
196+ "pr_updated_at" , entry .UpdatedAt .Format (time .RFC3339 ),
197+ "cache_file" , filepath .Base (cacheFile ))
198+ }
160199 }
161200 }
162201 }
0 commit comments