Use a branch root semispace cache

This commit is contained in:
Chris Penner 2024-07-10 22:57:09 -07:00
parent 987cd61976
commit 474fddbd13
5 changed files with 23 additions and 41 deletions

View File

@ -587,4 +587,4 @@ preloadProjectBranch codebase (ProjectAndBranch projectId branchId) = do
ch <- runTransaction codebase $ do
causalHashId <- Q.expectProjectBranchHead projectId branchId
Q.expectCausalHash causalHashId
preloadProjectRoot codebase ch
preloadBranch codebase ch

View File

@ -39,7 +39,6 @@ import Unison.Codebase.SqliteCodebase.Branch.Dependencies qualified as BD
import Unison.Codebase.SqliteCodebase.Migrations qualified as Migrations
import Unison.Codebase.SqliteCodebase.Operations qualified as CodebaseOps
import Unison.Codebase.SqliteCodebase.Paths
import Unison.Codebase.SqliteCodebase.ProjectRootCache qualified as ProjectRootCache
import Unison.Codebase.SqliteCodebase.SyncEphemeral qualified as SyncEphemeral
import Unison.Codebase.Type (LocalOrRemote (..))
import Unison.Codebase.Type qualified as C
@ -55,6 +54,7 @@ import Unison.Sqlite qualified as Sqlite
import Unison.Symbol (Symbol)
import Unison.Term (Term)
import Unison.Type (Type)
import Unison.Util.Cache qualified as Cache
import Unison.Util.Timing (time)
import Unison.WatchKind qualified as UF
import UnliftIO (UnliftIO (..), finally)
@ -164,8 +164,17 @@ sqliteCodebase ::
(Codebase m Symbol Ann -> m r) ->
m (Either Codebase1.OpenCodebaseError r)
sqliteCodebase debugName root localOrRemote lockOption migrationStrategy action = handleLockOption do
branchCache <- newBranchCache
projectRootCache <- ProjectRootCache.newProjectRootCache 5 {- Cache the last n project roots for quick switching. -}
-- The branchLoadCache ephemerally caches branches in memory, but doesn't prevent them from being GC'd.
-- This is very useful when loading root branches because the cache shouldn't be limited in size.
-- But this cache will automatically clean itself up and remove entries that are no longer reachable.
-- If you load another branch, which shares namespaces with another branch that's in memory (and therefor in the cache)
-- then those shared namespaces will be loaded from the cache and will be shared in memory.
branchLoadCache <- newBranchCache
-- The rootBranchCache is a semispace cache which keeps the most recent branch roots (e.g. project roots) alive in memory.
-- Unlike the branchLoadCache, this cache is bounded in size and will evict older branches when it reaches its limit.
-- The two work in tandem, so the rootBranchCache keeps relevant branches alive, and the branchLoadCache
-- stores ALL the subnamespaces of those branches, deduping them when loading from the DB.
rootBranchCache <- Cache.semispaceCache 10
getDeclType <- CodebaseOps.makeCachedTransaction 2048 CodebaseOps.getDeclType
-- The v1 codebase interface has operations to read and write individual definitions
-- whereas the v2 codebase writes them as complete components. These two fields buffer
@ -238,21 +247,23 @@ sqliteCodebase debugName root localOrRemote lockOption migrationStrategy action
-- if this blows up on cromulent hashes, then switch from `hashToHashId`
-- to one that returns Maybe.
getBranchForHash :: CausalHash -> m (Maybe (Branch m))
getBranchForHash h =
fmap (Branch.transform runTransaction) <$> runTransaction (CodebaseOps.getBranchForHash branchCache getDeclType h)
getBranchForHash =
Cache.applyDefined rootBranchCache \h -> do
fmap (Branch.transform runTransaction) <$> runTransaction (CodebaseOps.getBranchForHash branchLoadCache getDeclType h)
putBranch :: Branch m -> m ()
putBranch branch =
withRunInIO \runInIO ->
runInIO (runTransaction (CodebaseOps.putBranch (Branch.transform (Sqlite.unsafeIO . runInIO) branch)))
runInIO $ do
Cache.insert rootBranchCache (Branch.headHash branch) branch
runTransaction (CodebaseOps.putBranch (Branch.transform (Sqlite.unsafeIO . runInIO) branch))
preloadProjectRoot :: CausalHash -> m ()
preloadProjectRoot h = do
preloadBranch :: CausalHash -> m ()
preloadBranch h = do
void . UnliftIO.forkIO $ void $ do
getBranchForHash h >>= \case
Nothing -> pure ()
Just b -> do
ProjectRootCache.stashBranch projectRootCache b
UnliftIO.evaluate b
pure ()
@ -322,7 +333,7 @@ sqliteCodebase debugName root localOrRemote lockOption migrationStrategy action
termReferentsByPrefix = referentsByPrefix,
withConnection = withConn,
withConnectionIO = withConnection debugName root,
preloadProjectRoot
preloadBranch
}
Right <$> action codebase
where

View File

@ -1,28 +0,0 @@
-- | Simple cache which just keeps the last n relevant project branches in memory.
-- The Branch Cache handles all the lookups of the actual branch data by hash, this cache serves only to keep the last
-- n accessed branches in memory so they don't get garbage collected. See the Branch Cache for more context.
--
-- This speeds up switching back and forth between project branches, and also serves to keep the current project branch
-- in memory so it won't be cleaned up by the Branch Cache, since the Branch Cache only keeps
-- a weak reference to the current branch and we no longer keep the actual branch in LoopState.
module Unison.Codebase.SqliteCodebase.ProjectRootCache
( newProjectRootCache,
stashBranch,
)
where
import Control.Concurrent.STM
import Unison.Codebase.Branch
import Unison.Prelude
data ProjectRootCache m = ProjectRootCache {capacity :: Int, cached :: TVar [Branch m]}
newProjectRootCache :: (MonadIO m) => Int -> m (ProjectRootCache n)
newProjectRootCache capacity = do
var <- liftIO $ newTVarIO []
pure (ProjectRootCache capacity var)
stashBranch :: (MonadIO n) => ProjectRootCache m -> Branch m -> n ()
stashBranch ProjectRootCache {capacity, cached} branch = do
liftIO . atomically $ do
modifyTVar cached $ \branches -> take capacity (branch : filter (/= branch) branches)

View File

@ -85,7 +85,7 @@ data Codebase m v a = Codebase
-- soon, but not immediately. E.g. the user has switched a branch, but hasn't run any commands on it yet.
--
-- This combinator returns immediately, but warms the cache in the background with the desired branch.
preloadProjectRoot :: CausalHash -> m ()
preloadBranch :: CausalHash -> m ()
}
-- | Whether a codebase is local or remote.

View File

@ -81,7 +81,6 @@ library
Unison.Codebase.SqliteCodebase.Migrations.MigrateSchema7To8
Unison.Codebase.SqliteCodebase.Operations
Unison.Codebase.SqliteCodebase.Paths
Unison.Codebase.SqliteCodebase.ProjectRootCache
Unison.Codebase.SqliteCodebase.SyncEphemeral
Unison.Codebase.TermEdit
Unison.Codebase.TermEdit.Typing