repair single document upserts when using OP_MSG
780df80cfc
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.
This commit is contained in:
parent
fb0d140aa4
commit
995087e9a0
2 changed files with 20 additions and 1 deletions
|
@ -494,6 +494,9 @@ data FlagBit =
|
||||||
| ExhaustAllowed -- ^ The client is prepared for multiple replies to this request using the moreToCome bit.
|
| ExhaustAllowed -- ^ The client is prepared for multiple replies to this request using the moreToCome bit.
|
||||||
deriving (Show, Eq, Enum)
|
deriving (Show, Eq, Enum)
|
||||||
|
|
||||||
|
uOptDoc :: UpdateOption -> Document
|
||||||
|
uOptDoc Upsert = ["upsert" =: True]
|
||||||
|
uOptDoc MultiUpdate = ["multi" =: True]
|
||||||
|
|
||||||
{-
|
{-
|
||||||
OP_MSG header == 16 byte
|
OP_MSG header == 16 byte
|
||||||
|
@ -528,7 +531,7 @@ putOpMsg cmd requestId flagBit params = do
|
||||||
putCString "documents" -- identifier
|
putCString "documents" -- identifier
|
||||||
mapM_ putDocument iDocuments -- payload
|
mapM_ putDocument iDocuments -- payload
|
||||||
Update{..} -> do
|
Update{..} -> do
|
||||||
let doc = ["q" =: uSelector, "u" =: uUpdater]
|
let doc = ["q" =: uSelector, "u" =: uUpdater] <> concatMap uOptDoc uOptions
|
||||||
(sec0, sec1Size) =
|
(sec0, sec1Size) =
|
||||||
prepSectionInfo
|
prepSectionInfo
|
||||||
uFullCollection
|
uFullCollection
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
module QuerySpec (spec) where
|
module QuerySpec (spec) where
|
||||||
import Data.String (IsString(..))
|
import Data.String (IsString(..))
|
||||||
import TestImport
|
import TestImport
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Control.Monad (forM_, when)
|
import Control.Monad (forM_, when)
|
||||||
import System.Environment (getEnv)
|
import System.Environment (getEnv)
|
||||||
|
@ -87,6 +88,21 @@ spec = around withCleanDatabase $ do
|
||||||
db (count $ select ["name" =: "Yankees", "league" =: "American"] "team") `shouldReturn` 1
|
db (count $ select ["name" =: "Yankees", "league" =: "American"] "team") `shouldReturn` 1
|
||||||
_id `shouldBe` ()
|
_id `shouldBe` ()
|
||||||
|
|
||||||
|
describe "upsert" $ do
|
||||||
|
it "upserts a document twice with the same spec" $ do
|
||||||
|
let q = select ["name" =: "jack"] "users"
|
||||||
|
db $ upsert q ["color" =: "blue", "name" =: "jack"]
|
||||||
|
-- since there is no way to ask for a ack, we must wait for "a sufficient time"
|
||||||
|
-- for the write to be visible
|
||||||
|
threadDelay 10000
|
||||||
|
db (rest =<< find (select [] "users")) >>= print
|
||||||
|
db (count $ select ["name" =: "jack"] "users") `shouldReturn` 1
|
||||||
|
db $ upsert q ["color" =: "red", "name" =: "jack"]
|
||||||
|
threadDelay 10000
|
||||||
|
db (count $ select ["name" =: "jack"] "users") `shouldReturn` 1
|
||||||
|
Just doc <- db $ findOne (select ["name" =: "jack"] "users")
|
||||||
|
doc !? "color" `shouldBe` Just "red"
|
||||||
|
|
||||||
describe "insertMany" $ do
|
describe "insertMany" $ do
|
||||||
it "inserts documents to the collection and returns their _ids" $ do
|
it "inserts documents to the collection and returns their _ids" $ do
|
||||||
(_id1:_id2:_) <- db $ insertMany "team" [ ["name" =: "Yankees", "league" =: "American"]
|
(_id1:_id2:_) <- db $ insertMany "team" [ ["name" =: "Yankees", "league" =: "American"]
|
||||||
|
|
Loading…
Reference in a new issue