Skip to content

Commit 5abbe63

Browse files
authored
Merge pull request #17 from unisoncomputing/cp/fix-diff-caching
Fix caching for project and contribution diffs
2 parents 9a9b531 + 0e8ac4b commit 5abbe63

File tree

9 files changed

+127
-89
lines changed

9 files changed

+127
-89
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/Utils/Caching.hs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
module Share.Utils.Caching
88
( cachedResponse,
99
causalIdCacheKey,
10+
branchIdCacheKey,
1011
Cached,
1112
)
1213
where
@@ -15,13 +16,13 @@ import Data.ByteString qualified as BS
1516
import Data.ByteString.Lazy.Char8 qualified as BL
1617
import Data.Text.Encoding qualified as Text
1718
import Database.Redis qualified as R
18-
import Share.Postgres.IDs (CausalId (..))
19-
import Share.Prelude
20-
import Share.Web.App
21-
import Share.Web.Authorization qualified as AuthZ
2219
import Network.HTTP.Media
2320
import Network.HTTP.Types qualified as HTTP
2421
import Servant
22+
import Share.Postgres.IDs (BranchHashId (..), CausalId (..))
23+
import Share.Prelude
24+
import Share.Web.App
25+
import Share.Web.Authorization qualified as AuthZ
2526

2627
data Cached ct a
2728
= Cached BS.ByteString
@@ -128,3 +129,8 @@ causalIdCacheKey :: CausalId -> Text
128129
causalIdCacheKey (CausalId causalIdInt) =
129130
-- Causal Ids are globally unique and never re-used.
130131
"causal-id:" <> tShow @Int32 causalIdInt
132+
133+
branchIdCacheKey :: BranchHashId -> Text
134+
branchIdCacheKey (BranchHashId branchIdInt) =
135+
-- Branch Ids are globally unique and never re-used.
136+
"branch-id:" <> tShow @Int32 branchIdInt

src/Share/Web/Authorization.hs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module Share.Web.Authorization
2727
checkContributionCreate,
2828
checkContributionUpdate,
2929
checkContributionMerge,
30+
checkContributionDiffRead,
3031
checkContributionRead,
3132
checkContributionTimelineRead,
3233
checkCommentCreate,
@@ -465,7 +466,7 @@ checkProjectBranchRead reqUserId project@Project {projectId} =
465466

466467
checkProjectBranchDiff :: Maybe UserId -> Project -> WebApp (Either AuthZFailure AuthZReceipt)
467468
checkProjectBranchDiff reqUserId project@Project {projectId} =
468-
mapLeft (const authzError) <$> do
469+
bimap (const authzError) makeCacheable <$> do
469470
checkProjectGet reqUserId project
470471
where
471472
authzError = AuthZFailure $ (ProjectPermission (ProjectBranchDiff projectId))
@@ -541,6 +542,12 @@ checkContributionRead mayReqUserId project@(Project {projectId}) =
541542
where
542543
authzError = AuthZFailure $ ProjectPermission (ContributionRead projectId)
543544

545+
checkContributionDiffRead :: Maybe UserId -> Project -> WebApp (Either AuthZFailure AuthZReceipt)
546+
checkContributionDiffRead mayReqUserId project@(Project {projectId}) =
547+
bimap (const authzError) makeCacheable <$> checkProjectGet mayReqUserId project
548+
where
549+
authzError = AuthZFailure $ ProjectPermission (ContributionRead projectId)
550+
544551
checkContributionTimelineRead :: Maybe UserId -> Project -> WebApp (Either AuthZFailure AuthZReceipt)
545552
checkContributionTimelineRead mayReqUserId project@(Project {projectId}) =
546553
mapLeft (const authzError) <$> do
@@ -671,3 +678,9 @@ permissionGuard m =
671678
m >>= \case
672679
Right a -> pure a
673680
Left err -> Errors.respondError err
681+
682+
-- | Make an auth receipt cacheable.
683+
-- useful when we're re-using an existing auth receipt, but know that the current endpoint is
684+
-- cacheable for authed users even if the original isn't.
685+
makeCacheable :: AuthZReceipt -> AuthZReceipt
686+
makeCacheable (AuthZReceipt _) = AuthZReceipt (Just CachingToken)

src/Share/Web/Share/Contributions/Impl.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ contributionDiffEndpoint (AuthN.MaybeAuthedUserID mayCallerUserId) userHandle pr
232232
newBranch <- Q.branchById newBranchId `whenNothingM` throwError (EntityMissing (ErrorID "branch:missing") "Source branch not found")
233233
oldBranch <- Q.branchById oldBranchId `whenNothingM` throwError (EntityMissing (ErrorID "branch:missing") "Target branch not found")
234234
pure (project, contribution, oldBranch, newBranch)
235-
authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkContributionRead mayCallerUserId project
235+
authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkContributionDiffRead mayCallerUserId project
236236
let oldCodebase = Codebase.codebaseForProjectBranch authZReceipt project oldBranch
237237
let newCodebase = Codebase.codebaseForProjectBranch authZReceipt project newBranch
238238
oldPBSH <- Codebase.runCodebaseTransactionOrRespondError oldCodebase $ do
@@ -280,7 +280,7 @@ contributionDiffTermsEndpoint (AuthN.MaybeAuthedUserID mayCallerUserId) userHand
280280
newBranch <- Q.branchById newBranchId `whenNothingM` throwError (EntityMissing (ErrorID "branch:missing") "Source branch not found")
281281
oldBranch <- Q.branchById oldBranchId `whenNothingM` throwError (EntityMissing (ErrorID "branch:missing") "Target branch not found")
282282
pure (project, contribution, oldBranch, newBranch)
283-
authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkContributionRead mayCallerUserId project
283+
authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkContributionDiffRead mayCallerUserId project
284284
let oldCodebase = Codebase.codebaseForProjectBranch authZReceipt project oldBranch
285285
let newCodebase = Codebase.codebaseForProjectBranch authZReceipt project newBranch
286286
oldPBSH <- Codebase.runCodebaseTransactionOrRespondError oldCodebase $ do
@@ -325,7 +325,7 @@ contributionDiffTypesEndpoint (AuthN.MaybeAuthedUserID mayCallerUserId) userHand
325325
newBranch <- Q.branchById newBranchId `whenNothingM` throwError (EntityMissing (ErrorID "branch:missing") "Source branch not found")
326326
oldBranch <- Q.branchById oldBranchId `whenNothingM` throwError (EntityMissing (ErrorID "branch:missing") "Target branch not found")
327327
pure (project, contribution, oldBranch, newBranch)
328-
authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkContributionRead mayCallerUserId project
328+
authZReceipt <- AuthZ.permissionGuard $ AuthZ.checkContributionDiffRead mayCallerUserId project
329329
let oldCodebase = Codebase.codebaseForProjectBranch authZReceipt project oldBranch
330330
let newCodebase = Codebase.codebaseForProjectBranch authZReceipt project newBranch
331331
oldPBSH <- Codebase.runCodebaseTransactionOrRespondError oldCodebase $ do

0 commit comments

Comments
 (0)