Skip to content

Commit 33c5981

Browse files
committed
SyncV2 with Share server
1 parent d95114b commit 33c5981

File tree

17 files changed

+336
-68
lines changed

17 files changed

+336
-68
lines changed

codebase2/codebase-sqlite/U/Codebase/Sqlite/Queries.hs

+60-32
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ module U.Codebase.Sqlite.Queries
228228
expectEntity,
229229
syncToTempEntity,
230230
insertTempEntity,
231+
insertTempEntityV2,
231232
saveTempEntityInMain,
232233
expectTempEntity,
233234
deleteTempEntity,
@@ -315,6 +316,7 @@ import Data.Map.NonEmpty qualified as NEMap
315316
import Data.Maybe qualified as Maybe
316317
import Data.Sequence qualified as Seq
317318
import Data.Set qualified as Set
319+
import Data.Set.NonEmpty (NESet)
318320
import Data.Text qualified as Text
319321
import Data.Text.Encoding qualified as Text
320322
import Data.Text.Lazy qualified as Text.Lazy
@@ -532,23 +534,18 @@ countWatches = queryOneCol [sql| SELECT COUNT(*) FROM watch |]
532534

533535
saveHash :: Hash32 -> Transaction HashId
534536
saveHash hash = do
535-
execute
536-
[sql|
537-
INSERT INTO hash (base32) VALUES (:hash)
538-
ON CONFLICT DO NOTHING
539-
|]
540-
expectHashId hash
537+
loadHashId hash >>= \case
538+
Just h -> pure h
539+
Nothing -> do
540+
queryOneCol
541+
[sql|
542+
INSERT INTO hash (base32) VALUES (:hash)
543+
RETURNING id
544+
|]
541545

542546
saveHashes :: Traversable f => f Hash32 -> Transaction (f HashId)
543547
saveHashes hashes = do
544-
for_ hashes \hash ->
545-
execute
546-
[sql|
547-
INSERT INTO hash (base32)
548-
VALUES (:hash)
549-
ON CONFLICT DO NOTHING
550-
|]
551-
traverse expectHashId hashes
548+
for hashes saveHash
552549

553550
saveHashHash :: Hash -> Transaction HashId
554551
saveHashHash = saveHash . Hash32.fromHash
@@ -623,13 +620,15 @@ expectBranchHashForCausalHash ch = do
623620

624621
saveText :: Text -> Transaction TextId
625622
saveText t = do
626-
execute
627-
[sql|
628-
INSERT INTO text (text)
629-
VALUES (:t)
630-
ON CONFLICT DO NOTHING
631-
|]
632-
expectTextId t
623+
loadTextId t >>= \case
624+
Just h -> pure h
625+
Nothing -> do
626+
queryOneCol
627+
[sql|
628+
INSERT INTO text (text)
629+
VALUES (:t)
630+
RETURNING id
631+
|]
633632

634633
saveTexts :: Traversable f => f Text -> Transaction (f TextId)
635634
saveTexts =
@@ -686,7 +685,7 @@ saveObject ::
686685
ObjectType ->
687686
ByteString ->
688687
Transaction ObjectId
689-
saveObject hh h t blob = do
688+
saveObject _hh h t blob = do
690689
execute
691690
[sql|
692691
INSERT INTO object (primary_hash_id, type_id, bytes)
@@ -697,9 +696,9 @@ saveObject hh h t blob = do
697696
saveHashObject h oId 2 -- todo: remove this from here, and add it to other relevant places once there are v1 and v2 hashes
698697
rowsModified >>= \case
699698
0 -> pure ()
700-
_ -> do
701-
hash <- expectHash32 h
702-
tryMoveTempEntityDependents hh hash
699+
_ -> pure ()
700+
-- hash <- expectHash32 h
701+
-- tryMoveTempEntityDependents hh hash
703702
pure oId
704703

705704
expectObject :: SqliteExceptionReason e => ObjectId -> (ByteString -> Either e a) -> Transaction a
@@ -957,7 +956,7 @@ saveCausal ::
957956
BranchHashId ->
958957
[CausalHashId] ->
959958
Transaction ()
960-
saveCausal hh self value parents = do
959+
saveCausal _hh self value parents = do
961960
execute
962961
[sql|
963962
INSERT INTO causal (self_hash_id, value_hash_id)
@@ -973,27 +972,27 @@ saveCausal hh self value parents = do
973972
INSERT INTO causal_parent (causal_id, parent_id)
974973
VALUES (:self, :parent)
975974
|]
976-
flushCausalDependents hh self
975+
-- flushCausalDependents hh self
977976

978-
flushCausalDependents ::
977+
_flushCausalDependents ::
979978
HashHandle ->
980979
CausalHashId ->
981980
Transaction ()
982-
flushCausalDependents hh chId = do
981+
_flushCausalDependents hh chId = do
983982
hash <- expectHash32 (unCausalHashId chId)
984-
tryMoveTempEntityDependents hh hash
983+
_tryMoveTempEntityDependents hh hash
985984

986985
-- | `tryMoveTempEntityDependents #foo` does this:
987986
-- 0. Precondition: We just inserted object #foo.
988987
-- 1. Look up the dependents of #foo
989988
-- 2. Delete #foo as dependency from temp_entity_missing_dependency. e.g. (#bar, #foo), (#baz, #foo)
990989
-- 3. For each like #bar and #baz with no more rows in temp_entity_missing_dependency,
991990
-- insert_entity them.
992-
tryMoveTempEntityDependents ::
991+
_tryMoveTempEntityDependents ::
993992
HashHandle ->
994993
Hash32 ->
995994
Transaction ()
996-
tryMoveTempEntityDependents hh dependency = do
995+
_tryMoveTempEntityDependents hh dependency = do
997996
dependents <-
998997
queryListCol
999998
[sql|
@@ -2993,6 +2992,35 @@ insertTempEntity entityHash entity missingDependencies = do
29932992
entityType =
29942993
Entity.entityType entity
29952994

2995+
-- | Insert a new `temp_entity` row, and its associated 1+ `temp_entity_missing_dependency` rows.
2996+
--
2997+
-- Preconditions:
2998+
-- 1. The entity does not already exist in "main" storage (`object` / `causal`)
2999+
-- 2. The entity does not already exist in `temp_entity`.
3000+
insertTempEntityV2 :: Hash32 -> TempEntity -> NESet Hash32 -> Transaction ()
3001+
insertTempEntityV2 entityHash entity missingDependencies = do
3002+
execute
3003+
[sql|
3004+
INSERT INTO temp_entity (hash, blob, type_id)
3005+
VALUES (:entityHash, :entityBlob, :entityType)
3006+
ON CONFLICT DO NOTHING
3007+
|]
3008+
3009+
for_ missingDependencies \depHash ->
3010+
execute
3011+
[sql|
3012+
INSERT INTO temp_entity_missing_dependency (dependent, dependency)
3013+
VALUES (:entityHash, :depHash)
3014+
|]
3015+
where
3016+
entityBlob :: ByteString
3017+
entityBlob =
3018+
runPutS (Serialization.putTempEntity entity)
3019+
3020+
entityType :: TempEntityType
3021+
entityType =
3022+
Entity.entityType entity
3023+
29963024
-- | Delete a row from the `temp_entity` table, if it exists.
29973025
deleteTempEntity :: Hash32 -> Transaction ()
29983026
deleteTempEntity hash =

codebase2/codebase-sqlite/sql/001-temp-entity-tables.sql

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ create table if not exists temp_entity (
5656
create table if not exists temp_entity_missing_dependency (
5757
dependent text not null references temp_entity(hash),
5858
dependency text not null,
59-
dependencyJwt text not null,
59+
-- TODO: this is just for testing
60+
dependencyJwt text null,
6061
unique (dependent, dependency)
6162
);
6263
create index if not exists temp_entity_missing_dependency_ix_dependent on temp_entity_missing_dependency (dependent);

lib/unison-sqlite/package.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ library:
99

1010
dependencies:
1111
- base
12+
- containers
1213
- direct-sqlite
1314
- megaparsec
1415
- pretty-simple

lib/unison-sqlite/src/Unison/Sqlite/Connection.hs

+47-15
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ module Unison.Sqlite.Connection
5858
)
5959
where
6060

61+
import Data.Map qualified as Map
6162
import Database.SQLite.Simple qualified as Sqlite
6263
import Database.SQLite.Simple.FromField qualified as Sqlite
6364
import Database.SQLite3 qualified as Direct.Sqlite
@@ -71,7 +72,10 @@ import Unison.Sqlite.Connection.Internal (Connection (..))
7172
import Unison.Sqlite.Exception
7273
import Unison.Sqlite.Sql (Sql (..))
7374
import Unison.Sqlite.Sql qualified as Sql
75+
import UnliftIO (atomically)
7476
import UnliftIO.Exception
77+
import UnliftIO.STM (readTVar)
78+
import UnliftIO.STM qualified as STM
7579

7680
-- | Perform an action with a connection to a SQLite database.
7781
--
@@ -103,19 +107,47 @@ openConnection name file = do
103107
Just "" -> file
104108
_ -> "file:" <> file <> "?mode=ro"
105109
conn0 <- Sqlite.open sqliteURI `catch` rethrowAsSqliteConnectException name file
106-
let conn = Connection {conn = conn0, file, name}
110+
statementCache <- STM.newTVarIO Map.empty
111+
let conn = Connection {conn = conn0, file, name, statementCache}
107112
execute conn [Sql.sql| PRAGMA foreign_keys = ON |]
108113
execute conn [Sql.sql| PRAGMA busy_timeout = 60000 |]
114+
execute conn [Sql.sql| PRAGMA synchronous = normal |]
115+
execute conn [Sql.sql| PRAGMA journal_size_limit = 6144000 |]
116+
execute conn [Sql.sql| PRAGMA cache_size = -64000 |]
117+
execute conn [Sql.sql| PRAGMA temp_store = 2 |]
118+
109119
pure conn
110120

111121
-- Close a connection opened with 'openConnection'.
112122
closeConnection :: Connection -> IO ()
113-
closeConnection (Connection _ _ conn) =
123+
closeConnection conn@(Connection {conn = conn0}) = do
114124
-- FIXME if this throws an exception, it won't be under `SomeSqliteException`
115125
-- Possible fixes:
116126
-- 1. Add close exception to the hierarchy, e.g. `SqliteCloseException`
117127
-- 2. Always ignore exceptions thrown by `close` (Mitchell prefers this one)
118-
Sqlite.close conn
128+
closeAllStatements conn
129+
Sqlite.close conn0
130+
131+
withStatement :: Connection -> Text -> (Sqlite.Statement -> IO a) -> IO a
132+
withStatement conn sql action = do
133+
bracket (prepareStatement conn sql) Sqlite.reset action
134+
where
135+
prepareStatement :: Connection -> Text -> IO Sqlite.Statement
136+
prepareStatement Connection {conn, statementCache} sql = do
137+
cached <- atomically $ do
138+
cache <- STM.readTVar statementCache
139+
pure $ Map.lookup sql cache
140+
case cached of
141+
Just stmt -> pure stmt
142+
Nothing -> do
143+
stmt <- Sqlite.openStatement conn (coerce @Text @Sqlite.Query sql)
144+
atomically $ STM.modifyTVar statementCache (Map.insert sql stmt)
145+
pure stmt
146+
147+
closeAllStatements :: Connection -> IO ()
148+
closeAllStatements Connection {statementCache} = do
149+
cache <- atomically $ readTVar statementCache
150+
for_ cache Sqlite.closeStatement
119151

120152
-- An internal type, for making prettier debug logs
121153

@@ -152,7 +184,7 @@ logQuery (Sql sql params) result =
152184
-- Without results
153185

154186
execute :: (HasCallStack) => Connection -> Sql -> IO ()
155-
execute conn@(Connection _ _ conn0) sql@(Sql s params) = do
187+
execute conn sql@(Sql s params) = do
156188
logQuery sql Nothing
157189
doExecute `catch` \(exception :: Sqlite.SQLError) ->
158190
throwSqliteQueryException
@@ -163,16 +195,16 @@ execute conn@(Connection _ _ conn0) sql@(Sql s params) = do
163195
}
164196
where
165197
doExecute :: IO ()
166-
doExecute =
167-
Sqlite.withStatement conn0 (coerce s) \(Sqlite.Statement statement) -> do
168-
bindParameters statement params
169-
void (Direct.Sqlite.step statement)
198+
doExecute = do
199+
withStatement conn s \statement -> do
200+
bindParameters (coerce statement) params
201+
void (Direct.Sqlite.step $ coerce statement)
170202

171203
-- | Execute one or more semicolon-delimited statements.
172204
--
173205
-- This function does not support parameters, and is mostly useful for executing DDL and migrations.
174206
executeStatements :: (HasCallStack) => Connection -> Text -> IO ()
175-
executeStatements conn@(Connection _ _ (Sqlite.Connection database _tempNameCounter)) sql = do
207+
executeStatements conn@(Connection {conn = Sqlite.Connection database _tempNameCounter}) sql = do
176208
logQuery (Sql sql []) Nothing
177209
Direct.Sqlite.exec database sql `catch` \(exception :: Sqlite.SQLError) ->
178210
throwSqliteQueryException
@@ -185,7 +217,7 @@ executeStatements conn@(Connection _ _ (Sqlite.Connection database _tempNameCoun
185217
-- With results, without checks
186218

187219
queryStreamRow :: (HasCallStack, Sqlite.FromRow a) => Connection -> Sql -> (IO (Maybe a) -> IO r) -> IO r
188-
queryStreamRow conn@(Connection _ _ conn0) sql@(Sql s params) callback =
220+
queryStreamRow conn sql@(Sql s params) callback =
189221
run `catch` \(exception :: Sqlite.SQLError) ->
190222
throwSqliteQueryException
191223
SqliteQueryExceptionInfo
@@ -194,8 +226,8 @@ queryStreamRow conn@(Connection _ _ conn0) sql@(Sql s params) callback =
194226
sql
195227
}
196228
where
197-
run =
198-
bracket (Sqlite.openStatement conn0 (coerce s)) Sqlite.closeStatement \statement -> do
229+
run = do
230+
withStatement conn s \statement -> do
199231
Sqlite.bind statement params
200232
callback (Sqlite.nextRow statement)
201233

@@ -213,7 +245,7 @@ queryStreamCol =
213245
queryStreamRow
214246

215247
queryListRow :: forall a. (Sqlite.FromRow a, HasCallStack) => Connection -> Sql -> IO [a]
216-
queryListRow conn@(Connection _ _ conn0) sql@(Sql s params) = do
248+
queryListRow conn sql@(Sql s params) = do
217249
result <-
218250
doQuery
219251
`catch` \(exception :: Sqlite.SQLError) ->
@@ -228,7 +260,7 @@ queryListRow conn@(Connection _ _ conn0) sql@(Sql s params) = do
228260
where
229261
doQuery :: IO [a]
230262
doQuery =
231-
Sqlite.withStatement conn0 (coerce s) \statement -> do
263+
withStatement conn (coerce s) \statement -> do
232264
bindParameters (coerce statement) params
233265
let loop :: [a] -> IO [a]
234266
loop rows =
@@ -347,7 +379,7 @@ queryOneColCheck conn s check =
347379
-- Rows modified
348380

349381
rowsModified :: Connection -> IO Int
350-
rowsModified (Connection _ _ conn) =
382+
rowsModified (Connection {conn}) =
351383
Sqlite.changes conn
352384

353385
-- Vacuum

lib/unison-sqlite/src/Unison/Sqlite/Connection/Internal.hs

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ module Unison.Sqlite.Connection.Internal
33
)
44
where
55

6+
import Data.Map (Map)
7+
import Data.Text (Text)
68
import Database.SQLite.Simple qualified as Sqlite
9+
import UnliftIO.STM (TVar)
710

811
-- | A /non-thread safe/ connection to a SQLite database.
912
data Connection = Connection
1013
{ name :: String,
1114
file :: FilePath,
12-
conn :: Sqlite.Connection
15+
conn :: Sqlite.Connection,
16+
statementCache :: TVar (Map Text Sqlite.Statement)
1317
}
1418

1519
instance Show Connection where
16-
show (Connection name file _conn) =
20+
show (Connection name file _conn _statementCache) =
1721
"Connection { name = " ++ show name ++ ", file = " ++ show file ++ " }"

lib/unison-sqlite/unison-sqlite.cabal

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 1.12
22

3-
-- This file has been generated from package.yaml by hpack version 0.36.0.
3+
-- This file has been generated from package.yaml by hpack version 0.37.0.
44
--
55
-- see: https://github.com/sol/hpack
66

@@ -64,6 +64,7 @@ library
6464
ghc-options: -Wall
6565
build-depends:
6666
base
67+
, containers
6768
, direct-sqlite
6869
, megaparsec
6970
, pretty-simple

0 commit comments

Comments
 (0)