Skip to content

Commit 0e8ac4b

Browse files
committed
Parallelize a bunch of diff operations
1 parent 4883fe5 commit 0e8ac4b

File tree

4 files changed

+67
-55
lines changed

4 files changed

+67
-55
lines changed

src/Share/NamespaceDiffs.hs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{-# LANGUAGE ApplicativeDo #-}
2+
13
-- | Logic for computing the differerences between two namespaces,
24
-- typically used when showing the differences caused by a contribution.
35
module Share.NamespaceDiffs
@@ -23,6 +25,7 @@ import Data.Map qualified as Map
2325
import Data.Set qualified as Set
2426
import Data.Set.NonEmpty (NESet)
2527
import Data.Set.NonEmpty qualified as NESet
28+
import Servant (err500)
2629
import Share.Postgres qualified as PG
2730
import Share.Postgres.IDs (BranchHashId)
2831
import Share.Postgres.NameLookups.Conversions qualified as Cv
@@ -31,7 +34,6 @@ import Share.Postgres.NamespaceDiffs qualified as ND
3134
import Share.Prelude
3235
import Share.Utils.Logging qualified as Logging
3336
import Share.Web.Errors
34-
import Servant (err500)
3537
import U.Codebase.Reference qualified as V2
3638
import U.Codebase.Referent qualified as V2
3739
import Unison.Codebase.Path (Path)
@@ -82,7 +84,7 @@ data DefinitionDiffKind r
8284
RenamedFrom r (NESet Name)
8385
deriving stock (Eq, Show, Ord, Functor, Foldable, Traversable)
8486

85-
instance Ord r => Semigroup (DefinitionDiffs Name r) where
87+
instance (Ord r) => Semigroup (DefinitionDiffs Name r) where
8688
d1 <> d2 =
8789
DefinitionDiffs
8890
{ added = added d1 <> added d2,
@@ -92,7 +94,7 @@ instance Ord r => Semigroup (DefinitionDiffs Name r) where
9294
newAliases = Map.unionWith (\(a1, b1) (a2, b2) -> (a1 <> a2, b1 <> b2)) (newAliases d1) (newAliases d2)
9395
}
9496

95-
instance Ord r => Monoid (DefinitionDiffs Name r) where
97+
instance (Ord r) => Monoid (DefinitionDiffs Name r) where
9698
mempty =
9799
DefinitionDiffs
98100
{ added = mempty,
@@ -133,26 +135,26 @@ data DiffAtPath referent reference = DiffAtPath
133135
deriving stock (Eq, Show)
134136

135137
-- | A traversal over all the referents in a `DiffAtPath`.
136-
diffAtPathReferents_ :: Ord referent' => Traversal (DiffAtPath referent reference) (DiffAtPath referent' reference) referent referent'
138+
diffAtPathReferents_ :: (Ord referent') => Traversal (DiffAtPath referent reference) (DiffAtPath referent' reference) referent referent'
137139
diffAtPathReferents_ f (DiffAtPath {termDiffsAtPath, typeDiffsAtPath}) =
138140
termDiffsAtPath
139141
& (Set.traverse . traverse) %%~ f
140142
& fmap \termDiffsAtPath -> DiffAtPath {typeDiffsAtPath, termDiffsAtPath}
141143

142144
-- | A traversal over all the references in a `DiffAtPath`.
143-
diffAtPathReferences_ :: Ord reference' => Traversal (DiffAtPath referent reference) (DiffAtPath referent reference') reference reference'
145+
diffAtPathReferences_ :: (Ord reference') => Traversal (DiffAtPath referent reference) (DiffAtPath referent reference') reference reference'
144146
diffAtPathReferences_ f (DiffAtPath {termDiffsAtPath, typeDiffsAtPath}) =
145147
typeDiffsAtPath
146148
& (Set.traverse . traverse) %%~ f
147149
& fmap \typeDiffsAtPath -> DiffAtPath {typeDiffsAtPath, termDiffsAtPath}
148150

149151
-- | Traversal over all the referents in a `NamespaceTreeDiff`.
150-
namespaceTreeDiffReferents_ :: Ord referent' => Traversal (NamespaceTreeDiff referent reference) (NamespaceTreeDiff referent' reference) referent referent'
152+
namespaceTreeDiffReferents_ :: (Ord referent') => Traversal (NamespaceTreeDiff referent reference) (NamespaceTreeDiff referent' reference) referent referent'
151153
namespaceTreeDiffReferents_ =
152154
traversed . traversed . diffAtPathReferents_
153155

154156
-- | Traversal over all the references in a `NamespaceTreeDiff`.
155-
namespaceTreeDiffReferences_ :: Ord reference' => Traversal (NamespaceTreeDiff referent reference) (NamespaceTreeDiff referent reference') reference reference'
157+
namespaceTreeDiffReferences_ :: (Ord reference') => Traversal (NamespaceTreeDiff referent reference) (NamespaceTreeDiff referent reference') reference reference'
156158
namespaceTreeDiffReferences_ = traversed . traversed . diffAtPathReferences_
157159

158160
data NamespaceDiffError = ImpossibleError Text
@@ -170,8 +172,10 @@ instance Logging.Loggable NamespaceDiffError where
170172
-- Note: This ignores all dependencies in the lib namespace.
171173
diffTreeNamespaces :: (BranchHashId, NameLookupReceipt) -> (BranchHashId, NameLookupReceipt) -> (PG.Transaction e (Either NamespaceDiffError (NamespaceTreeDiff V2.Referent V2.Reference)))
172174
diffTreeNamespaces (oldBHId, oldNLReceipt) (newBHId, newNLReceipt) = do
173-
(oldTerms, newTerms) <- ND.getRelevantTermsForDiff oldNLReceipt oldBHId newBHId
174-
(oldTypes, newTypes) <- ND.getRelevantTypesForDiff newNLReceipt oldBHId newBHId
175+
((oldTerms, newTerms), (oldTypes, newTypes)) <- PG.pipelined do
176+
terms <- ND.getRelevantTermsForDiff oldNLReceipt oldBHId newBHId
177+
types <- ND.getRelevantTypesForDiff newNLReceipt oldBHId newBHId
178+
pure (terms, types)
175179
case diffTreeNamespacesHelper (oldTerms, newTerms) (oldTypes, newTypes) of
176180
Left e -> pure $ Left e
177181
Right nd ->
@@ -263,7 +267,7 @@ compressNameTree (diffs Cofree.:< children) =
263267
-- | Compute changes between two unstructured Name relations, determining what has changed and how
264268
-- it should be interpreted so it's meaningful to the user.
265269
computeDefinitionDiff ::
266-
Ord ref =>
270+
(Ord ref) =>
267271
Relation Name ref {- Relevant definitions from old namespace -} ->
268272
Relation Name ref {- Relevant definitions from new namespace -} ->
269273
Either NamespaceDiffError (DefinitionDiffs Name ref)
@@ -326,7 +330,7 @@ computeDefinitionDiff old new =
326330
)
327331

328332
-- | Convert a `DefinitionDiffs` into a tree of differences.
329-
definitionDiffsToTree :: forall ref. Ord ref => DefinitionDiffs Name ref -> Cofree (Map NameSegment) (Map NameSegment (Set (DefinitionDiff ref)))
333+
definitionDiffsToTree :: forall ref. (Ord ref) => DefinitionDiffs Name ref -> Cofree (Map NameSegment) (Map NameSegment (Set (DefinitionDiff ref)))
330334
definitionDiffsToTree dd =
331335
let DefinitionDiffs {added, removed, updated, renamed, newAliases} = dd
332336
expandedAliases :: Map Name (Set (DefinitionDiffKind ref))

src/Share/Postgres.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,7 @@ instance (QueryM m) => QueryM (MaybeT m) where
372372
prepareStatements :: Bool
373373
prepareStatements = True
374374

375-
queryListRows :: forall r m. (Interp.DecodeRow r, QueryM m) => Interp.Sql -> m [r]
375+
queryListRows :: forall r m. (Interp.DecodeRow r, QueryA m) => Interp.Sql -> m [r]
376376
queryListRows sql = statement () (Interp.interp prepareStatements sql)
377377

378378
query1Row :: forall r m. (QueryM m) => (Interp.DecodeRow r) => Interp.Sql -> m (Maybe r)

src/Share/Postgres/NamespaceDiffs.hs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE ApplicativeDo #-}
12
{-# LANGUAGE TypeOperators #-}
23

34
module Share.Postgres.NamespaceDiffs
@@ -7,7 +8,6 @@ module Share.Postgres.NamespaceDiffs
78
where
89

910
import Data.Either qualified as Either
10-
import Share.Postgres (Transaction)
1111
import Share.Postgres qualified as PG
1212
import Share.Postgres.IDs (BranchHashId)
1313
import Share.Postgres.NameLookups.Types (NameLookupReceipt, NamedRef (..), ReversedName)
@@ -25,11 +25,11 @@ import Unison.Util.Relation qualified as Rel
2525
-- 3. Names that are in both namespaces, but have different refs
2626
-- 4. Refs that are in both namespaces, but have different names
2727
getRelevantTermsForDiff ::
28+
(PG.QueryA m) =>
2829
NameLookupReceipt ->
2930
BranchHashId ->
3031
BranchHashId ->
31-
Transaction
32-
e
32+
m
3333
( Relation Name PGReferent {- relevant terms in old namespace -},
3434
Relation Name PGReferent {- relevant terms only in new namespace -}
3535
)
@@ -42,9 +42,8 @@ getRelevantTermsForDiff !_nameLookupReceipt oldBranchHashId newBranchHashId = do
4242
-- 4. Find (name, ref) pairs that are in both namespaces, but have different names
4343
-- 5. Return the results as a list of (ref, name, isNew) tuples. It's possible for the same
4444
-- (name, ref) pair to appear with both (isNew = true) and (isNew = false) in the result.
45-
rows <-
46-
PG.queryListRows @(NamedRef PGReferent PG.:. PG.Only Bool)
47-
[PG.sql|
45+
PG.queryListRows @(NamedRef PGReferent PG.:. PG.Only Bool)
46+
[PG.sql|
4847
WITH only_in_old AS (
4948
( SELECT old.reversed_name, old.referent_builtin, old.referent_component_hash_id, old.referent_component_index, old.referent_constructor_index
5049
FROM scoped_term_name_lookup old
@@ -107,12 +106,15 @@ getRelevantTermsForDiff !_nameLookupReceipt oldBranchHashId newBranchHashId = do
107106
SELECT new.reversed_name, new.referent_builtin, new.referent_component_hash_id, new.referent_component_index, new.referent_constructor_index, true
108107
FROM relevant_terms_in_new new
109108
|]
110-
<&> fmap \(NamedRef {reversedSegments, ref} PG.:. PG.Only inNew) ->
111-
if inNew
112-
then Right (from @ReversedName @Name reversedSegments, ref)
113-
else Left (from @ReversedName @Name reversedSegments, ref)
114-
let (old, new) = Either.partitionEithers rows
115-
pure $ (Rel.fromList old, Rel.fromList new)
109+
<&> ( fmap \(NamedRef {reversedSegments, ref} PG.:. PG.Only inNew) ->
110+
if inNew
111+
then Right (from @ReversedName @Name reversedSegments, ref)
112+
else
113+
Left (from @ReversedName @Name reversedSegments, ref)
114+
)
115+
<&> \rows ->
116+
let (old, new) = Either.partitionEithers rows
117+
in (Rel.fromList old, Rel.fromList new)
116118

117119
-- | Gets the types relevant for computing the diff between two branches.
118120
-- Where 'relevant' is defined as:
@@ -121,7 +123,7 @@ getRelevantTermsForDiff !_nameLookupReceipt oldBranchHashId newBranchHashId = do
121123
-- 2. Types that are in the new namespace but not the old namespace
122124
-- 3. Names that are in both namespaces, but have different refs
123125
-- 4. Refs that are in both namespaces, but have different names
124-
getRelevantTypesForDiff :: NameLookupReceipt -> BranchHashId -> BranchHashId -> Transaction e (Relation Name PGReference, Relation Name PGReference)
126+
getRelevantTypesForDiff :: (PG.QueryA m) => NameLookupReceipt -> BranchHashId -> BranchHashId -> m (Relation Name PGReference, Relation Name PGReference)
125127
getRelevantTypesForDiff !_nameLookupReceipt oldBranchHashId newBranchHashId = do
126128
-- This SQL query does the following:
127129
--
@@ -131,9 +133,8 @@ getRelevantTypesForDiff !_nameLookupReceipt oldBranchHashId newBranchHashId = do
131133
-- 4. Find (name, ref) pairs that are in both namespaces, but have different names
132134
-- 5. Return the results as a list of (ref, name, isNew) tuples. It's possible for the same
133135
-- (name, ref) pair to appear with both (isNew = true) and (isNew = false) in the result.
134-
rows <-
135-
PG.queryListRows @(NamedRef PGReference PG.:. PG.Only Bool)
136-
[PG.sql|
136+
PG.queryListRows @(NamedRef PGReference PG.:. PG.Only Bool)
137+
[PG.sql|
137138
WITH only_in_old AS (
138139
( SELECT old.reversed_name, old.reference_builtin, old.reference_component_hash_id, old.reference_component_index
139140
FROM scoped_type_name_lookup old
@@ -192,9 +193,11 @@ getRelevantTypesForDiff !_nameLookupReceipt oldBranchHashId newBranchHashId = do
192193
SELECT new.reversed_name, new.reference_builtin, new.reference_component_hash_id, new.reference_component_index, true
193194
FROM relevant_types_in_new new
194195
|]
195-
<&> fmap \(NamedRef {reversedSegments, ref} PG.:. PG.Only inNew) ->
196-
if inNew
197-
then Right (from @ReversedName @Name reversedSegments, ref)
198-
else Left (from @ReversedName @Name reversedSegments, ref)
199-
let (old, new) = Either.partitionEithers rows
200-
pure $ (Rel.fromList old, Rel.fromList new)
196+
<&> ( fmap \(NamedRef {reversedSegments, ref} PG.:. PG.Only inNew) ->
197+
if inNew
198+
then Right (from @ReversedName @Name reversedSegments, ref)
199+
else Left (from @ReversedName @Name reversedSegments, ref)
200+
)
201+
<&> \rows ->
202+
let (old, new) = Either.partitionEithers rows
203+
in (Rel.fromList old, Rel.fromList new)

src/Share/Web/Share/Diffs/Impl.hs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import Unison.Server.Types (DisplayObjectDiff, TermDefinition (..), TermTag, Typ
3333
import Unison.ShortHash (ShortHash)
3434
import Unison.Syntax.Name qualified as Name
3535
import Unison.Util.Pretty (Width)
36+
import UnliftIO qualified
3637

3738
diffNamespaces ::
3839
AuthZReceipt ->
@@ -66,16 +67,16 @@ diffCausals ::
6667
diffCausals !_authZReceipt oldCausalId newCausalId = do
6768
-- Ensure name lookups for each thing we're diffing.
6869
-- We do this in two separate transactions to ensure we can still make progress even if we need to build name lookups.
69-
(oldBranchHashId, oldBranchNLReceipt) <- PG.runTransaction $ do
70-
oldBranchHashId <- CausalQ.expectNamespaceIdsByCausalIdsOf id oldCausalId
71-
oldBranchNLReceipt <- NLOps.ensureNameLookupForBranchId oldBranchHashId
72-
pure (oldBranchHashId, oldBranchNLReceipt)
73-
74-
(newBranchHashId, newNLReceipt) <- PG.runTransaction $ do
75-
newBranchHashId <- CausalQ.expectNamespaceIdsByCausalIdsOf id newCausalId
76-
newNLReceipt <- NLOps.ensureNameLookupForBranchId newBranchHashId
77-
pure (newBranchHashId, newNLReceipt)
70+
let getOldBranch = PG.runTransaction $ do
71+
oldBranchHashId <- CausalQ.expectNamespaceIdsByCausalIdsOf id oldCausalId
72+
oldBranchNLReceipt <- NLOps.ensureNameLookupForBranchId oldBranchHashId
73+
pure (oldBranchHashId, oldBranchNLReceipt)
7874

75+
let getNewBranch = PG.runTransaction $ do
76+
newBranchHashId <- CausalQ.expectNamespaceIdsByCausalIdsOf id newCausalId
77+
newNLReceipt <- NLOps.ensureNameLookupForBranchId newBranchHashId
78+
pure (newBranchHashId, newNLReceipt)
79+
((oldBranchHashId, oldBranchNLReceipt), (newBranchHashId, newNLReceipt)) <- getOldBranch `UnliftIO.concurrently` getNewBranch
7980
PG.runTransactionOrRespondError $ do
8081
diff <- NamespaceDiffs.diffTreeNamespaces (oldBranchHashId, oldBranchNLReceipt) (newBranchHashId, newNLReceipt) `whenLeftM` throwError
8182
withTermTags <-
@@ -100,12 +101,12 @@ diffTerms ::
100101
(Codebase.CodebaseEnv, BranchHashId, Name) ->
101102
(Codebase.CodebaseEnv, BranchHashId, Name) ->
102103
WebApp (TermDefinition, TermDefinition, DisplayObjectDiff)
103-
diffTerms !_authZReceipt old@(_, _, oldName) new@(_, _, newName) =
104-
do
105-
oldTerm@(TermDefinition {termDefinition = oldDisplayObj}) <- getTermDefinition old `whenNothingM` respondError (EntityMissing (ErrorID "term-not-found") ("'From' term not found: " <> Name.toText oldName))
106-
newTerm@(TermDefinition {termDefinition = newDisplayObj}) <- getTermDefinition new `whenNothingM` respondError (EntityMissing (ErrorID "term-not-found") ("'To' term not found: " <> Name.toText newName))
107-
let termDiffDisplayObject = DefinitionDiff.diffDisplayObjects oldDisplayObj newDisplayObj
108-
pure $ (oldTerm, newTerm, termDiffDisplayObject)
104+
diffTerms !_authZReceipt old@(_, _, oldName) new@(_, _, newName) = do
105+
let getOldTerm = getTermDefinition old `whenNothingM` respondError (EntityMissing (ErrorID "term-not-found") ("'From' term not found: " <> Name.toText oldName))
106+
let getNewTerm = getTermDefinition new `whenNothingM` respondError (EntityMissing (ErrorID "term-not-found") ("'To' term not found: " <> Name.toText newName))
107+
(oldTerm, newTerm) <- getOldTerm `UnliftIO.concurrently` getNewTerm
108+
let termDiffDisplayObject = DefinitionDiff.diffDisplayObjects (termDefinition oldTerm) (termDefinition newTerm)
109+
pure $ (oldTerm, newTerm, termDiffDisplayObject)
109110
where
110111
renderWidth :: Width
111112
renderWidth = 80
@@ -124,12 +125,16 @@ diffTypes ::
124125
(Codebase.CodebaseEnv, BranchHashId, Name) ->
125126
(Codebase.CodebaseEnv, BranchHashId, Name) ->
126127
WebApp (TypeDefinition, TypeDefinition, DisplayObjectDiff)
127-
diffTypes !_authZReceipt old@(_, _, oldTypeName) new@(_, _, newTypeName) =
128-
do
129-
sourceType@(TypeDefinition {typeDefinition = sourceDisplayObj}) <- getTypeDefinition old `whenNothingM` respondError (EntityMissing (ErrorID "type-not-found") ("'From' Type not found: " <> Name.toText oldTypeName))
130-
newType@(TypeDefinition {typeDefinition = newDisplayObj}) <- getTypeDefinition new `whenNothingM` respondError (EntityMissing (ErrorID "type-not-found") ("'To' Type not found: " <> Name.toText newTypeName))
131-
let typeDiffDisplayObject = DefinitionDiff.diffDisplayObjects sourceDisplayObj newDisplayObj
132-
pure $ (sourceType, newType, typeDiffDisplayObject)
128+
diffTypes !_authZReceipt old@(_, _, oldTypeName) new@(_, _, newTypeName) = do
129+
let getOldType =
130+
getTypeDefinition old
131+
`whenNothingM` respondError (EntityMissing (ErrorID "type-not-found") ("'From' Type not found: " <> Name.toText oldTypeName))
132+
let getNewType =
133+
getTypeDefinition new
134+
`whenNothingM` respondError (EntityMissing (ErrorID "type-not-found") ("'To' Type not found: " <> Name.toText newTypeName))
135+
(sourceType, newType) <- getOldType `UnliftIO.concurrently` getNewType
136+
let typeDiffDisplayObject = DefinitionDiff.diffDisplayObjects (typeDefinition sourceType) (typeDefinition newType)
137+
pure $ (sourceType, newType, typeDiffDisplayObject)
133138
where
134139
renderWidth :: Width
135140
renderWidth = 80

0 commit comments

Comments
 (0)