refactor to support early exit when no media changes

- regular sync now receives a media USN as well
- server bumps media usn not only on sync but any other media change
- added local media.hasChanged()
This commit is contained in:
Damien Elmes 2011-10-06 15:34:22 +09:00
parent dd37ee5915
commit 0c85acf3f7
3 changed files with 31 additions and 17 deletions

View file

@ -175,6 +175,9 @@ If the same name exists, compare checksums."""
def clearLog(self): def clearLog(self):
self.db.execute("delete from log") self.db.execute("delete from log")
def hasChanged(self):
return self.db.scalar("select 1 from log limit 1")
# Tracking changes (private) # Tracking changes (private)
########################################################################## ##########################################################################

View file

@ -47,8 +47,8 @@ class Syncer(object):
"Returns 'noChanges', 'fullSync', or 'success'." "Returns 'noChanges', 'fullSync', or 'success'."
# step 1: login & metadata # step 1: login & metadata
self.status("login") self.status("login")
self.rmod, rscm, self.maxUsn, rts = self.server.meta() self.rmod, rscm, self.maxUsn, rts, self.mediaUsn = self.server.meta()
self.lmod, lscm, self.minUsn, lts = self.meta() self.lmod, lscm, self.minUsn, lts, dummy = self.meta()
if abs(rts - lts) > 300: if abs(rts - lts) > 300:
return "clockOff" return "clockOff"
if self.lmod == self.rmod: if self.lmod == self.rmod:
@ -90,7 +90,7 @@ class Syncer(object):
return "success" return "success"
def meta(self): def meta(self):
return (self.deck.mod, self.deck.scm, self.deck._usn, intTime()) return (self.deck.mod, self.deck.scm, self.deck._usn, intTime(), None)
def changes(self): def changes(self):
"Bundle up deletions and small objects, and apply if server." "Bundle up deletions and small objects, and apply if server."
@ -535,21 +535,25 @@ class MediaSyncer(object):
self.server = server self.server = server
self.added = None self.added = None
def sync(self): def sync(self, mediaUsn):
# step 1: send/recv deletions # step 1: check if there have been any changes
self.deck.media.findChanges()
lusn = self.deck.media.usn()
if lusn == mediaUsn and not self.deck.media.hasChanged():
return "noChanges"
# step 2: send/recv deletions
runHook("mediaSync", "remove") runHook("mediaSync", "remove")
usn = self.deck.media.usn()
lrem = self.removed() lrem = self.removed()
rrem = self.server.remove(fnames=lrem, minUsn=usn) rrem = self.server.remove(fnames=lrem, minUsn=lusn)
self.remove(rrem) self.remove(rrem)
# step 2: stream files from server # step 3: stream files from server
runHook("mediaSync", "server") runHook("mediaSync", "server")
while 1: while 1:
runHook("mediaSync", "stream") runHook("mediaSync", "stream")
zip = self.server.files() zip = self.server.files()
if self.addFiles(zip=zip) != "continue": if self.addFiles(zip=zip) != "continue":
break break
# step 3: stream files to the server # step 4: stream files to the server
runHook("mediaSync", "client") runHook("mediaSync", "client")
while 1: while 1:
runHook("mediaSync", "stream") runHook("mediaSync", "stream")
@ -558,10 +562,12 @@ class MediaSyncer(object):
if usn != "continue": if usn != "continue":
# when server has run out of files, it returns bumped usn # when server has run out of files, it returns bumped usn
break break
# step 5: finalize
self.deck.media.setUsn(usn) self.deck.media.setUsn(usn)
self.deck.media.clearLog() self.deck.media.clearLog()
# clear cursor so successive calls work # clear cursor so successive calls work
self.added = None self.added = None
return "success"
def removed(self): def removed(self):
return self.deck.media.removed() return self.deck.media.removed()

View file

@ -19,6 +19,7 @@ deck1=None
deck2=None deck2=None
client=None client=None
server=None server=None
server2=None
def setup_basic(): def setup_basic():
global deck1, deck2, client, server global deck1, deck2, client, server
@ -271,7 +272,7 @@ def setup_remote():
def test_meta(): def test_meta():
global TEST_REMOTE global TEST_REMOTE
try: try:
(mod, scm, usn, ts) = server.meta() (mod, scm, usn, ts, dummy) = server.meta()
except Exception, e: except Exception, e:
if e.errno == 61: if e.errno == 61:
TEST_REMOTE = False TEST_REMOTE = False
@ -330,9 +331,10 @@ def test_remoteSync():
# the current directory is the media folder. # the current directory is the media folder.
def setup_remoteMedia(): def setup_remoteMedia():
global client, server global client, server, server2
setup_basic() setup_basic()
server = RemoteMediaServer(TEST_HKEY) server = RemoteMediaServer(TEST_HKEY)
server2 = RemoteServer(TEST_USER, TEST_HKEY)
client = MediaSyncer(deck1, server) client = MediaSyncer(deck1, server)
@nose.with_setup(setup_remoteMedia) @nose.with_setup(setup_remoteMedia)
@ -340,26 +342,29 @@ def test_media():
server.mediatest("reset") server.mediatest("reset")
assert len(os.listdir(deck1.media.dir())) == 0 assert len(os.listdir(deck1.media.dir())) == 0
assert server.mediatest("count") == 0 assert server.mediatest("count") == 0
# initially, nothing to do
assert client.sync(server2.meta()[4]) == "noChanges"
# add a file # add a file
time.sleep(1)
os.chdir(deck1.media.dir()) os.chdir(deck1.media.dir())
p = os.path.join(deck1.media.dir(), "foo.jpg") p = os.path.join(deck1.media.dir(), "foo.jpg")
open(p, "wb").write("foo") open(p, "wb").write("foo")
assert len(os.listdir(deck1.media.dir())) == 1 assert len(os.listdir(deck1.media.dir())) == 1
assert server.mediatest("count") == 0 assert server.mediatest("count") == 0
client.sync() assert client.sync(server2.meta()[4]) == "success"
time.sleep(1) time.sleep(1)
# should have been synced # should have been synced
assert len(os.listdir(deck1.media.dir())) == 1 assert len(os.listdir(deck1.media.dir())) == 1
assert server.mediatest("count") == 1 assert server.mediatest("count") == 1
# if we remove the file, should be removed # if we remove the file, should be removed
os.unlink(p) os.unlink(p)
client.sync() assert client.sync(server2.meta()[4]) == "success"
assert len(os.listdir(deck1.media.dir())) == 0 assert len(os.listdir(deck1.media.dir())) == 0
assert server.mediatest("count") == 0 assert server.mediatest("count") == 0
# we should be able to add it again # we should be able to add it again
time.sleep(1) time.sleep(1)
open(p, "wb").write("foo") open(p, "wb").write("foo")
client.sync() assert client.sync(server2.meta()[4]) == "success"
assert len(os.listdir(deck1.media.dir())) == 1 assert len(os.listdir(deck1.media.dir())) == 1
assert server.mediatest("count") == 1 assert server.mediatest("count") == 1
# if we modify it, it should get sent too. also we set the zip size very # if we modify it, it should get sent too. also we set the zip size very
@ -369,7 +374,7 @@ def test_media():
open(p, "wb").write("bar") open(p, "wb").write("bar")
open(p+"2", "wb").write("baz") open(p+"2", "wb").write("baz")
assert len(os.listdir(deck1.media.dir())) == 2 assert len(os.listdir(deck1.media.dir())) == 2
client.sync() client.sync(server2.meta()[4])
assert len(os.listdir(deck1.media.dir())) == 2 assert len(os.listdir(deck1.media.dir())) == 2
assert server.mediatest("count") == 2 assert server.mediatest("count") == 2
# if we lose our media db, we should be able to bring it back in sync # if we lose our media db, we should be able to bring it back in sync
@ -379,12 +384,12 @@ def test_media():
deck1.media.connect() deck1.media.connect()
changes = deck1.media.added().fetchall() changes = deck1.media.added().fetchall()
assert len(changes) == 2 assert len(changes) == 2
client.sync() client.sync(server2.meta()[4])
assert len(os.listdir(deck1.media.dir())) == 2 assert len(os.listdir(deck1.media.dir())) == 2
assert server.mediatest("count") == 2 assert server.mediatest("count") == 2
# if we send an unchanged file, the server should cope # if we send an unchanged file, the server should cope
time.sleep(1) time.sleep(1)
deck1.media.db.execute("insert into log values ('foo.jpg', 0)") deck1.media.db.execute("insert into log values ('foo.jpg', 0)")
client.sync() client.sync(server2.meta()[4])
assert len(os.listdir(deck1.media.dir())) == 2 assert len(os.listdir(deck1.media.dir())) == 2
assert server.mediatest("count") == 2 assert server.mediatest("count") == 2