Skip to content

Commit 995087e

Browse files
committed
repair single document upserts when using OP_MSG
780df80 introduces support for the OP_MSG protocol. Unfortunately, the upsert and multi options of the update command still use flagBits to communicate the options, whereas they must be provided directly into the command document, alongside the "q" and "v" fields. This commit: - introduces a test for a single-document upsert that, if isolated, succeeds against the reference MongoDB 3.6 container, but fails against an official 6.0 image. - provides a patch that sets the appropriate options. The test is not perfect as the upsert operation is inherently racy and this difficult to test. A comfortable threadDelay has been inserted as a workaround to accomodate for medium workloads.
1 parent fb0d140 commit 995087e

File tree

2 files changed

+20
-1
lines changed

2 files changed

+20
-1
lines changed

Database/MongoDB/Internal/Protocol.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,9 @@ data FlagBit =
494494
| ExhaustAllowed -- ^ The client is prepared for multiple replies to this request using the moreToCome bit.
495495
deriving (Show, Eq, Enum)
496496

497+
uOptDoc :: UpdateOption -> Document
498+
uOptDoc Upsert = ["upsert" =: True]
499+
uOptDoc MultiUpdate = ["multi" =: True]
497500

498501
{-
499502
OP_MSG header == 16 byte
@@ -528,7 +531,7 @@ putOpMsg cmd requestId flagBit params = do
528531
putCString "documents" -- identifier
529532
mapM_ putDocument iDocuments -- payload
530533
Update{..} -> do
531-
let doc = ["q" =: uSelector, "u" =: uUpdater]
534+
let doc = ["q" =: uSelector, "u" =: uUpdater] <> concatMap uOptDoc uOptions
532535
(sec0, sec1Size) =
533536
prepSectionInfo
534537
uFullCollection

test/QuerySpec.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module QuerySpec (spec) where
55
import Data.String (IsString(..))
66
import TestImport
7+
import Control.Concurrent (threadDelay)
78
import Control.Exception
89
import Control.Monad (forM_, when)
910
import System.Environment (getEnv)
@@ -87,6 +88,21 @@ spec = around withCleanDatabase $ do
8788
db (count $ select ["name" =: "Yankees", "league" =: "American"] "team") `shouldReturn` 1
8889
_id `shouldBe` ()
8990

91+
describe "upsert" $ do
92+
it "upserts a document twice with the same spec" $ do
93+
let q = select ["name" =: "jack"] "users"
94+
db $ upsert q ["color" =: "blue", "name" =: "jack"]
95+
-- since there is no way to ask for a ack, we must wait for "a sufficient time"
96+
-- for the write to be visible
97+
threadDelay 10000
98+
db (rest =<< find (select [] "users")) >>= print
99+
db (count $ select ["name" =: "jack"] "users") `shouldReturn` 1
100+
db $ upsert q ["color" =: "red", "name" =: "jack"]
101+
threadDelay 10000
102+
db (count $ select ["name" =: "jack"] "users") `shouldReturn` 1
103+
Just doc <- db $ findOne (select ["name" =: "jack"] "users")
104+
doc !? "color" `shouldBe` Just "red"
105+
90106
describe "insertMany" $ do
91107
it "inserts documents to the collection and returns their _ids" $ do
92108
(_id1:_id2:_) <- db $ insertMany "team" [ ["name" =: "Yankees", "league" =: "American"]

0 commit comments

Comments
 (0)