Skip to content

Stabilize the build system by correctly house keeping the dirtykeys and rule values [flaky test #4185 #4093] #4190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 53 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
684a850
passing keys need to be update directly to restartShakeSession
soulomoon Apr 22, 2024
5d09837
send actions to run between restart
soulomoon Apr 24, 2024
13528d7
fix
soulomoon Apr 24, 2024
fdbb7aa
fix
soulomoon Apr 24, 2024
6fc3646
some more fix up
soulomoon Apr 24, 2024
e247ae1
use IO [Key]
soulomoon Apr 24, 2024
7b7ea4d
remove double return
soulomoon Apr 24, 2024
c31a375
Update ghcide/src/Development/IDE/Core/FileExists.hs
soulomoon Apr 26, 2024
bfb06a3
minor fix
soulomoon Apr 26, 2024
8adf5a4
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon Apr 26, 2024
bbc5c95
capture more dirty keys to between sessions
soulomoon Apr 27, 2024
48d5644
cleanup
soulomoon Apr 27, 2024
e967dde
fix the race between cache value updated but not updated hls-database
soulomoon Apr 28, 2024
69c9396
fix build
soulomoon Apr 28, 2024
02f0d41
fix hls-graph
soulomoon Apr 28, 2024
554102d
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon Apr 28, 2024
c983727
fix 9.2.8
soulomoon Apr 28, 2024
3748fc2
format
soulomoon Apr 29, 2024
3107879
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon Apr 30, 2024
a65ac5c
run refreshDeps in a single asyncWithCleanUp
soulomoon May 1, 2024
f7a15cb
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon May 1, 2024
f4690c5
shut the session before shut the reactor
soulomoon May 1, 2024
c6a33cb
Merge remote-tracking branch 'upstream/soulomoon/mark-dirty-keys-sync…
soulomoon May 1, 2024
e6105ff
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon May 2, 2024
610355c
Revert "shut the session before shut the reactor"
soulomoon May 2, 2024
63b1956
remove record dirty key recordDirtyKeys
soulomoon May 2, 2024
4d28344
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon May 4, 2024
2eb29b4
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon May 5, 2024
2c61a63
Merge branch 'master' into soulomoon/mark-dirty-keys-sync-to-hls-graph
soulomoon May 5, 2024
7423695
Update ghcide/src/Development/IDE/Core/Shake.hs
soulomoon May 6, 2024
0c4a2f5
Update ghcide/src/Development/IDE/Core/FileExists.hs
soulomoon May 6, 2024
bea88b5
Update ghcide/session-loader/Development/IDE/Session.hs
soulomoon May 6, 2024
c9219f0
Update ghcide/session-loader/Development/IDE/Session.hs
soulomoon May 6, 2024
7a08b03
cleanup
soulomoon May 6, 2024
dc18b74
fix
soulomoon May 6, 2024
dc71a40
cleanup
soulomoon May 6, 2024
342f52f
fix ghc 9.2
soulomoon May 6, 2024
240254e
stylish
soulomoon May 6, 2024
797d9e9
push back extendKnownTargets to shake restart
soulomoon May 6, 2024
7704d6a
Update FileExists.hs
soulomoon May 8, 2024
ee1c334
update doc
soulomoon May 8, 2024
e7d380b
add comment
soulomoon May 8, 2024
e0a7ff7
Update Shake.hs
soulomoon May 9, 2024
a410dd9
Update Shake.hs
soulomoon May 9, 2024
cc1fa28
Update Shake.hs
soulomoon May 9, 2024
ebce5eb
Update Shake.hs
soulomoon May 9, 2024
24ec73f
Update Session.hs
soulomoon May 9, 2024
d609b34
Update Shake.hs
soulomoon May 9, 2024
91f88b3
Update Shake.hs
soulomoon May 9, 2024
db969c8
Update Main.hs
soulomoon May 9, 2024
2eb20d2
fix comment
soulomoon May 9, 2024
035a71c
fix import
soulomoon May 9, 2024
f4f80f7
Fix note references
jhrcek May 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 13 additions & 18 deletions ghcide/session-loader/Development/IDE/Session.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
import Database.SQLite.Simple
import Development.IDE.Core.Tracing (withTrace)
import Development.IDE.Session.Diagnostics (renderCradleError)
import Development.IDE.Types.Shake (WithHieDb)
import Development.IDE.Types.Shake (WithHieDb, toNoFileKey)
import HieDb.Create
import HieDb.Types
import HieDb.Utils
Expand Down Expand Up @@ -474,10 +474,9 @@
clientConfig <- getClientConfigAction
extras@ShakeExtras{restartShakeSession, ideNc, knownTargetsVar, lspEnv
} <- getShakeExtras
let invalidateShakeCache :: IO ()
invalidateShakeCache = do
let invalidateShakeCache = do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this in fact invalidate anything any more?

void $ modifyVar' version succ
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be moved out of the restart.

join $ atomically $ recordDirtyKeys extras GhcSessionIO [emptyFilePath]
return $ toNoFileKey GhcSessionIO

IdeOptions{ optTesting = IdeTesting optTesting
, optCheckProject = getCheckProject
Expand Down Expand Up @@ -510,16 +509,16 @@
TargetModule _ -> do
found <- filterM (IO.doesFileExist . fromNormalizedFilePath) targetLocations
return [(targetTarget, Set.fromList found)]
hasUpdate <- join $ atomically $ do
hasUpdate <- atomically $ do
known <- readTVar knownTargetsVar
let known' = flip mapHashed known $ \k ->
HM.unionWith (<>) k $ HM.fromList knownTargets
hasUpdate = if known /= known' then Just (unhashed known') else Nothing
writeTVar knownTargetsVar known'
logDirtyKeys <- recordDirtyKeys extras GetKnownTargets [emptyFilePath]
return (logDirtyKeys >> pure hasUpdate)
pure hasUpdate
for_ hasUpdate $ \x ->
logWith recorder Debug $ LogKnownFilesUpdated x
return $ toNoFileKey GetKnownTargets

-- Create a new HscEnv from a hieYaml root and a set of options
let packageSetup :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath)
Expand Down Expand Up @@ -612,18 +611,14 @@
, "If you are using a .cabal file, please ensure that this module is listed in either the exposed-modules or other-modules section"
]

void $ modifyVar' fileToFlags $
Map.insert hieYaml this_flags_map
void $ modifyVar' filesMap $
flip HM.union (HM.fromList (map ((,hieYaml) . fst) $ concatMap toFlagsMap all_targets))

void $ extendKnownTargets all_targets

-- Invalidate all the existing GhcSession build nodes by restarting the Shake session
invalidateShakeCache

void $ modifyVar' fileToFlags $ Map.insert hieYaml this_flags_map
void $ modifyVar' filesMap $ flip HM.union (HM.fromList (map ((,hieYaml) . fst) $ concatMap toFlagsMap all_targets))
-- The VFS doesn't change on cradle edits, re-use the old one.
restartShakeSession VFSUnmodified "new component" []
-- Invalidate all the existing GhcSession build nodes by restarting the Shake session
keys2 <- invalidateShakeCache
restartShakeSession VFSUnmodified "new component" [] $ do
keys1 <- extendKnownTargets all_targets
return [keys1, keys2]

-- Typecheck all files in the project on startup
checkProject <- getCheckProject
Expand Down Expand Up @@ -678,7 +673,7 @@
InstallationMismatch{..} ->
return (([renderPackageSetupException cfp GhcVersionMismatch{..}], Nothing),[])
InstallationChecked _compileTime _ghcLibCheck -> do
atomicModifyIORef' cradle_files (\xs -> (cfp:xs,()))

Check warning on line 676 in ghcide/session-loader/Development/IDE/Session.hs

View workflow job for this annotation

GitHub Actions / Hlint check run

Warning in loadSessionWithOptions in module Development.IDE.Session: Use atomicModifyIORef'_ ▫︎ Found: "atomicModifyIORef' cradle_files (\\ xs -> (cfp : xs, ()))" ▫︎ Perhaps: "atomicModifyIORef'_ cradle_files ((:) cfp)"
session (hieYaml, toNormalizedFilePath' cfp, opts, libDir)
-- Failure case, either a cradle error or the none cradle
Left err -> do
Expand Down
15 changes: 8 additions & 7 deletions ghcide/src/Development/IDE/Core/FileExists.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import qualified Development.IDE.Core.Shake as Shake
import Development.IDE.Graph
import Development.IDE.Types.Location
import Development.IDE.Types.Options
import Development.IDE.Types.Shake (toKey)
import qualified Focus
import Ide.Logger (Pretty (pretty),
Recorder, WithPriority,
Expand Down Expand Up @@ -105,12 +106,12 @@ getFileExistsMapUntracked = do
FileExistsMapVar v <- getIdeGlobalAction
return v

-- | Modify the global store of file exists.
modifyFileExists :: IdeState -> [(NormalizedFilePath, FileChangeType)] -> IO ()
-- | Modify the global store of file exists and return the keys that need to be marked as dirty
modifyFileExists :: IdeState -> [(NormalizedFilePath, FileChangeType)] -> IO [Key]
modifyFileExists state changes = do
FileExistsMapVar var <- getIdeGlobalState state
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think getIdeGlobalState could potentially be in STM, in which case this whole thing could be in STM.

-- Masked to ensure that the previous values are flushed together with the map update
join $ mask_ $ atomicallyNamed "modifyFileExists" $ do
mask_ $ atomicallyNamed "modifyFileExists" $ do
forM_ changes $ \(f,c) ->
case fromChange c of
Just c' -> STM.focus (Focus.insert c') f var
Expand All @@ -119,10 +120,10 @@ modifyFileExists state changes = do
-- flush previous values
let (fileModifChanges, fileExistChanges) =
partition ((== FileChangeType_Changed) . snd) changes
mapM_ (deleteValue (shakeExtras state) GetFileExists . fst) fileExistChanges
io1 <- recordDirtyKeys (shakeExtras state) GetFileExists $ map fst fileExistChanges
io2 <- recordDirtyKeys (shakeExtras state) GetModificationTime $ map fst fileModifChanges
return (io1 <> io2)
keys0 <- concat <$> mapM (deleteValue (shakeExtras state) GetFileExists . fst) fileExistChanges
let keys1 = map (toKey GetFileExists . fst) fileExistChanges
let keys2 = map (toKey GetModificationTime . fst) fileModifChanges
return (keys0 <> keys1 <> keys2)

fromChange :: FileChangeType -> Maybe Bool
fromChange FileChangeType_Created = Just True
Expand Down
38 changes: 19 additions & 19 deletions ghcide/src/Development/IDE/Core/FileStore.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import Development.IDE.Import.DependencyInformation
import Development.IDE.Types.Diagnostics
import Development.IDE.Types.Location
import Development.IDE.Types.Options
import Development.IDE.Types.Shake (toKey)
import HieDb.Create (deleteMissingRealFiles)
import Ide.Logger (Pretty (pretty),
Priority (Info),
Expand Down Expand Up @@ -148,24 +149,24 @@ isInterface :: NormalizedFilePath -> Bool
isInterface f = takeExtension (fromNormalizedFilePath f) `elem` [".hi", ".hi-boot", ".hie", ".hie-boot", ".core"]

-- | Reset the GetModificationTime state of interface files
resetInterfaceStore :: ShakeExtras -> NormalizedFilePath -> STM ()
resetInterfaceStore :: ShakeExtras -> NormalizedFilePath -> STM [Key]
resetInterfaceStore state f = do
deleteValue state GetModificationTime f

-- | Reset the GetModificationTime state of watched files
-- Assumes the list does not include any FOIs
resetFileStore :: IdeState -> [(NormalizedFilePath, LSP.FileChangeType)] -> IO ()
resetFileStore :: IdeState -> [(NormalizedFilePath, LSP.FileChangeType)] -> IO [Key]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be in STM, I think? Unless it's deliberately not to reduce contention.

resetFileStore ideState changes = mask $ \_ -> do
-- we record FOIs document versions in all the stored values
-- so NEVER reset FOIs to avoid losing their versions
-- FOI filtering is done by the caller (LSP Notification handler)
forM_ changes $ \(nfp, c) -> do
case c of
LSP.FileChangeType_Changed
-- already checked elsewhere | not $ HM.member nfp fois
-> atomically $
deleteValue (shakeExtras ideState) GetModificationTime nfp
_ -> pure ()
fmap concat <$>
forM changes $ \(nfp, c) -> do
case c of
LSP.FileChangeType_Changed
-- already checked elsewhere | not $ HM.member nfp fois
-> atomically $ deleteValue (shakeExtras ideState) GetModificationTime nfp
_ -> pure []


modificationTime :: FileVersion -> Maybe UTCTime
Expand Down Expand Up @@ -215,16 +216,18 @@ setFileModified :: Recorder (WithPriority Log)
-> IdeState
-> Bool -- ^ Was the file saved?
-> NormalizedFilePath
-> IO [Key]
-> IO ()
setFileModified recorder vfs state saved nfp = do
setFileModified recorder vfs state saved nfp actionBefore = do
ideOptions <- getIdeOptionsIO $ shakeExtras state
doCheckParents <- optCheckParents ideOptions
let checkParents = case doCheckParents of
AlwaysCheck -> True
CheckOnSave -> saved
_ -> False
join $ atomically $ recordDirtyKeys (shakeExtras state) GetModificationTime [nfp]
restartShakeSession (shakeExtras state) vfs (fromNormalizedFilePath nfp ++ " (modified)") []
restartShakeSession (shakeExtras state) vfs (fromNormalizedFilePath nfp ++ " (modified)") [] $ do
keys<-actionBefore
return (toKey GetModificationTime nfp:keys)
when checkParents $
typecheckParents recorder state nfp

Expand All @@ -244,14 +247,11 @@ typecheckParentsAction recorder nfp = do
-- | Note that some keys have been modified and restart the session
-- Only valid if the virtual file system was initialised by LSP, as that
-- independently tracks which files are modified.
setSomethingModified :: VFSModified -> IdeState -> [Key] -> String -> IO ()
setSomethingModified vfs state keys reason = do
setSomethingModified :: VFSModified -> IdeState -> String -> IO [Key] -> IO ()
setSomethingModified vfs state reason actionBetweenSession = do
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, it mostly computes dirty keys, right?

-- Update database to remove any files that might have been renamed/deleted
atomically $ do
writeTQueue (indexQueue $ hiedbWriter $ shakeExtras state) (\withHieDb -> withHieDb deleteMissingRealFiles)
modifyTVar' (dirtyKeys $ shakeExtras state) $ \x ->
foldl' (flip insertKeySet) x keys
void $ restartShakeSession (shakeExtras state) vfs reason []
atomically $ writeTQueue (indexQueue $ hiedbWriter $ shakeExtras state) (\withHieDb -> withHieDb deleteMissingRealFiles)
void $ restartShakeSession (shakeExtras state) vfs reason [] actionBetweenSession

registerFileWatches :: [String] -> LSP.LspT Config IO Bool
registerFileWatches globs = do
Expand Down
13 changes: 8 additions & 5 deletions ghcide/src/Development/IDE/Core/OfInterest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import Development.IDE.Plugin.Completions.Types
import Development.IDE.Types.Exports
import Development.IDE.Types.Location
import Development.IDE.Types.Options (IdeTesting (..))
import Development.IDE.Types.Shake (toKey)
import GHC.TypeLits (KnownSymbol)
import Ide.Logger (Pretty (pretty),
Priority (..),
Expand Down Expand Up @@ -103,24 +104,26 @@ getFilesOfInterestUntracked = do
OfInterestVar var <- getIdeGlobalAction
liftIO $ readVar var

addFileOfInterest :: IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO ()
addFileOfInterest :: IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO [Key]
addFileOfInterest state f v = do
OfInterestVar var <- getIdeGlobalState state
(prev, files) <- modifyVar var $ \dict -> do
let (prev, new) = HashMap.alterF (, Just v) f dict
pure (new, (prev, new))
when (prev /= Just v) $ do
join $ atomically $ recordDirtyKeys (shakeExtras state) IsFileOfInterest [f]
if prev /= Just v
then do
logWith (ideLogger state) Debug $
LogSetFilesOfInterest (HashMap.toList files)
return [toKey IsFileOfInterest f]
else return []

deleteFileOfInterest :: IdeState -> NormalizedFilePath -> IO ()
deleteFileOfInterest :: IdeState -> NormalizedFilePath -> IO [Key]
deleteFileOfInterest state f = do
OfInterestVar var <- getIdeGlobalState state
files <- modifyVar' var $ HashMap.delete f
join $ atomically $ recordDirtyKeys (shakeExtras state) IsFileOfInterest [f]
logWith (ideLogger state) Debug $
LogSetFilesOfInterest (HashMap.toList files)
return [toKey IsFileOfInterest f]
scheduleGarbageCollection :: IdeState -> IO ()
scheduleGarbageCollection state = do
GarbageCollectVar var <- getIdeGlobalState state
Expand Down
66 changes: 45 additions & 21 deletions ghcide/src/Development/IDE/Core/Shake.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
FileVersion(..),
updatePositionMapping,
updatePositionMappingHelper,
deleteValue, recordDirtyKeys,
deleteValue,
WithProgressFunc, WithIndefiniteProgressFunc,
ProgressEvent(..),
DelayedAction, mkDelayedAction,
Expand Down Expand Up @@ -123,7 +123,7 @@
import Development.IDE.Core.ProgressReporting
import Development.IDE.Core.RuleTypes
import Development.IDE.Core.Tracing
import Development.IDE.GHC.Compat (NameCache,

Check warning on line 126 in ghcide/src/Development/IDE/Core/Shake.hs

View workflow job for this annotation

GitHub Actions / Hlint check run

Warning in module Development.IDE.Core.Shake: Use fewer imports ▫︎ Found: "import Development.IDE.GHC.Compat\n ( NameCache, NameCacheUpdater(..), initNameCache, knownKeyNames )\nimport Development.IDE.GHC.Compat\n ( mkSplitUniqSupply, upNameCache )\n" ▫︎ Perhaps: "import Development.IDE.GHC.Compat\n ( NameCache,\n NameCacheUpdater(..),\n initNameCache,\n knownKeyNames,\n mkSplitUniqSupply,\n upNameCache )\n"
NameCacheUpdater (..),
initNameCache,
knownKeyNames)
Expand Down Expand Up @@ -300,6 +300,7 @@
:: VFSModified
-> String
-> [DelayedAction ()]
-> IO [Key]
-> IO ()
#if MIN_VERSION_ghc(9,3,0)
,ideNc :: NameCache
Expand Down Expand Up @@ -557,26 +558,17 @@


-- | Delete the value stored for a given ide build key
-- and return the key that was deleted.
deleteValue
:: Shake.ShakeValue k
=> ShakeExtras
-> k
-> NormalizedFilePath
-> STM ()
deleteValue ShakeExtras{dirtyKeys, state} key file = do
-> STM [Key]
deleteValue ShakeExtras{state} key file = do
STM.delete (toKey key file) state
modifyTVar' dirtyKeys $ insertKeySet (toKey key file)
return [toKey key file]

recordDirtyKeys
:: Shake.ShakeValue k
=> ShakeExtras
-> k
-> [NormalizedFilePath]
-> STM (IO ())
recordDirtyKeys ShakeExtras{dirtyKeys} key file = do
modifyTVar' dirtyKeys $ \x -> foldl' (flip insertKeySet) x (toKey key <$> file)
return $ withEventTrace "recordDirtyKeys" $ \addEvent -> do
addEvent (fromString $ unlines $ "dirty " <> show key : map fromNormalizedFilePath file)

-- | We return Nothing if the rule has not run and Just Failed if it has failed to produce a value.
getValues ::
Expand Down Expand Up @@ -759,12 +751,16 @@
-- | Restart the current 'ShakeSession' with the given system actions.
-- Any actions running in the current session will be aborted,
-- but actions added via 'shakeEnqueue' will be requeued.
shakeRestart :: Recorder (WithPriority Log) -> IdeState -> VFSModified -> String -> [DelayedAction ()] -> IO ()
shakeRestart recorder IdeState{..} vfs reason acts =
shakeRestart :: Recorder (WithPriority Log) -> IdeState -> VFSModified -> String -> [DelayedAction ()] -> IO [Key] -> IO ()
shakeRestart recorder IdeState{..} vfs reason acts ioActionBetweenShakeSession =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more like "compute dirty keys", right?

withMVar'
shakeSession
(\runner -> do
(stopTime,()) <- duration $ logErrorAfter 10 $ cancelShakeSession runner
keys <- ioActionBetweenShakeSession
-- it is every important to update the dirty keys after we enter the critical section
-- see Note [Housekeeping rule cache and dirty key outside of hls-graph]
atomically $ modifyTVar' (dirtyKeys shakeExtras) $ \x -> foldl' (flip insertKeySet) x keys
res <- shakeDatabaseProfile shakeDb
backlog <- readTVarIO $ dirtyKeys shakeExtras
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we even need the dirtyKeys TVar now? I believe we now only set it just above and then we immediately read it!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I am thinking about removing it too, but our garbage collector depend on it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have to keep it this way for now. Maybe until we find a better way to handle the garbage collector, but this should be in another PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks to me like the garbage collector calls getDirtySet which gets the dirty keys from hls-graph?

Copy link
Collaborator Author

@soulomoon soulomoon May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but it also mark the key as dirty in shakeExtra's dirtykeys at foldM (removeDirtyKey dirtyKeys state) (0,[]) agedKeys accompany to the rule cache removal.

Copy link
Collaborator Author

@soulomoon soulomoon May 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is odd since we might have cleared it in the rule cahce,
but a part of the rule cache would still be staying in the hls-graph database.
We might want to improve this in the future.

queue <- atomicallyNamed "actionQueue - peek" $ peekInProgress $ actionQueue shakeExtras
Expand Down Expand Up @@ -1198,7 +1194,7 @@
Just (v@(Succeeded _ x), diags) -> do
ver <- estimateFileVersionUnsafely key (Just x) file
doDiagnostics (vfsVersion =<< ver) $ Vector.toList diags
return $ Just $ RunResult ChangedNothing old $ A v
return $ Just $ RunResult ChangedNothing old (A v) $ return ()
_ -> return Nothing
_ ->
-- assert that a "clean" rule is never a cache miss
Expand All @@ -1222,7 +1218,6 @@
Nothing -> do
pure (toShakeValue ShakeStale mbBs, staleV)
Just v -> pure (maybe ShakeNoCutoff ShakeResult mbBs, Succeeded ver v)
liftIO $ atomicallyNamed "define - write" $ setValues state key file res (Vector.fromList diags)
doDiagnostics (vfsVersion =<< ver) diags
let eq = case (bs, fmap decodeShakeValue mbOld) of
(ShakeResult a, Just (ShakeResult b)) -> cmp a b
Expand All @@ -1232,9 +1227,12 @@
_ -> False
return $ RunResult
(if eq then ChangedRecomputeSame else ChangedRecomputeDiff)
(encodeShakeValue bs) $
A res
liftIO $ atomicallyNamed "define - dirtyKeys" $ modifyTVar' dirtyKeys (deleteKeySet $ toKey key file)
(encodeShakeValue bs)
(A res) $ do
-- this hook needs to be run in the same transaction as the key is marked clean
-- see Note [Housekeeping rule cache and dirty key outside of hls-graph]
setValues state key file res (Vector.fromList diags)
modifyTVar' dirtyKeys (deleteKeySet $ toKey key file)
return res
where
-- Highly unsafe helper to compute the version of a file
Expand All @@ -1258,6 +1256,32 @@
-- * creating bogus "file does not exists" diagnostics
| otherwise = useWithoutDependency (GetModificationTime_ False) fp

-- Note [Housekeeping rule cache and dirty key outside of hls-graph]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Hls-graph contains its own internal running state for each key in the shakeDatabase.
-- ShakeExtras contains `state` field (rule result cache) and `dirtyKeys` (keys that became
-- dirty in between build sessions) that is not visible to the hls-graph
-- Essentially, we need to keep the rule cache and dirty key and hls-graph's internal state
-- in sync.

-- 1. A dirty key collected in a session should not be removed from dirty keys in the same session.
-- Since if we clean out the dirty key in the same session,
-- 1.1. we will lose the chance to dirty its reverse dependencies. Since it only happens during session restart.
-- 1.2. a key might be marked as dirty in ShakeExtras while it's being recomputed by hls-graph which could lead to it's premature removal from dirtyKeys.
-- See issue https://github.com/haskell/haskell-language-server/issues/4093 for more details.

-- 2. When a key is marked clean in the hls-graph's internal running
-- state, the rule cache and dirty keys are updated in the same transaction.
-- otherwise, some situations like the following can happen:
-- thread 1: hls-graph session run a key
-- thread 1: defineEarlyCutoff' run the action for the key
-- thread 1: the action is done, rule cache and dirty key are updated
-- thread 2: we restart the hls-graph session, thread 1 is killed, the
-- hls-graph's internal state is not updated.
-- This is problematic with early cut off because we are having a new rule cache matching the
-- old hls-graph's internal state, which might case it's reverse dependency to skip the recomputation.
-- See https://github.com/haskell/haskell-language-server/issues/4194 for more details.

traceA :: A v -> String
traceA (A Failed{}) = "Failed"
traceA (A Stale{}) = "Stale"
Expand Down
2 changes: 1 addition & 1 deletion ghcide/src/Development/IDE/Core/Tracing.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
-- | Returns a logger that produces telemetry events in a single span.
telemetryLogRecorder :: SpanInFlight -> Recorder (WithPriority (Doc a))
telemetryLogRecorder sp = Recorder $ \WithPriority {..} ->
liftIO $ addEvent sp (fromString $ show priority) (encodeUtf8 $ trim $ renderStrict $ layoutCompact $ payload)

Check warning on line 64 in ghcide/src/Development/IDE/Core/Tracing.hs

View workflow job for this annotation

GitHub Actions / Hlint check run

Suggestion in telemetryLogRecorder in module Development.IDE.Core.Tracing: Redundant $ ▫︎ Found: "layoutCompact $ payload" ▫︎ Perhaps: "layoutCompact payload"
where
-- eventlog message size is limited by EVENT_PAYLOAD_SIZE_MAX = STG_WORD16_MAX
trim = T.take (fromIntegral(maxBound :: Word16) - 10)
Expand Down Expand Up @@ -112,7 +112,7 @@
ExitCaseSuccess res -> do
setTag sp "result" (pack $ result $ runValue res)
setTag sp "changed" $ case res of
RunResult x _ _ -> fromString $ show x
RunResult x _ _ _ -> fromString $ show x
endSpan sp)
(\sp -> act (liftIO . setTag sp "diagnostics" . encodeUtf8 . showDiagnostics ))
| otherwise = act (\_ -> return ())
Expand Down
Loading
Loading