only call resetParentCommits() on dirstate write

Summary:
This updates the Eden mercurial extension to no longer invoke the Eden
`resetParentCommits()` thrift call when `setparents()` is called on the
dirstate map.  Instead we now defer the call to `resetParentCommits()` until
`write()` is called to write the dirstate data to disk.

Informing edenfs of the parent change as soon as `setparents()` was called was
problematic, as this made edenfs reflect the change before the transaction was
committed.  Some mercurial commands, notably `hg status` also call
`setparents()` on the dirstate but never write this back to disk at all.  This
is problematic since `hg status` calls `setparents()` without holding any
mercurial locks.  As a result it may call `setparents()` with the "wrong"
parent if another mercurial process is running and is in the middle of a
transaction.

Reviewed By: bolinfest, chadaustin

Differential Revision: D7980375

fbshipit-source-id: 4f5e4391fd291d4ea5fc93bb9d49ed0380fc1721
This commit is contained in:
Adam Simpkins 2018-05-14 11:58:21 -07:00 committed by Facebook Github Bot
parent abe68df349
commit c8e69b61fb

View File

@ -8,6 +8,7 @@
# of patent rights can be found in the PATENTS file in the same directory.
import os
import threading
from eden.integration.lib import eden_server_inspector, hgrepo
@ -248,3 +249,50 @@ class RebaseTest(EdenHgTestCase):
+ str(num_slow_path)
),
)
def test_rebase_with_concurrent_status(self):
"""
Test using `hg rebase` to rebase a stack while simultaneously running
`hg status`
"""
stop = threading.Event()
def status_thread():
while not stop.is_set():
self.hg("status", stdout=None, stderr=None)
# Spawn several threads to run "hg status" in parallel with the rebase
num_threads = 6
threads = []
for _ in range(num_threads):
t = threading.Thread(target=status_thread)
threads.append(t)
t.start()
# Run the rebase. Explicitly disable inmemory rebase so that eden
# will need to update the working directory state as tehe rebase progresses
self.hg(
"--debug",
"--config",
"rebase.experimental.inmemory=False",
"rebase",
"-s",
self._c11,
"-d",
self._c25,
stdout=None,
stderr=None,
)
new_commit = self.hg("log", "-rtip", "-T{node}")
stop.set()
for t in threads:
t.join()
self.assert_status_empty()
# Verify that the new commit looks correct
self.repo.update(new_commit)
self.assert_status_empty()
self.assert_file_regex("numbers/1/15", "15\n")
self.assert_file_regex("numbers/2/25", "25\n")