mirror of
https://github.com/ankitects/anki.git
synced 2025-09-21 23:42:23 -04:00
move sanity check to server and automatically force full sync
- server will log mismatches so we don't require people to post on the forums anymore or manually force a full sync - if we find problems like missing notes, report that in the sanity check so the server can keep track of problems - when the server detects a conflict it can immediately abort the sync, so a subsequent sync will no longer report a conflict
This commit is contained in:
parent
634b28388c
commit
8d66578d43
2 changed files with 35 additions and 18 deletions
49
anki/sync.py
49
anki/sync.py
|
@ -133,13 +133,16 @@ class Syncer(object):
|
||||||
self.server.applyChunk(chunk=chunk)
|
self.server.applyChunk(chunk=chunk)
|
||||||
if chunk['done']:
|
if chunk['done']:
|
||||||
break
|
break
|
||||||
# step 5: sanity check during beta testing
|
# step 5: sanity check
|
||||||
runHook("sync", "sanity")
|
runHook("sync", "sanity")
|
||||||
c = self.sanityCheck()
|
c = self.sanityCheck()
|
||||||
s = self.server.sanityCheck()
|
ret = self.server.sanityCheck2(client=c)
|
||||||
if c != s:
|
if ret['status'] != "ok":
|
||||||
raise Exception("""\
|
# roll back and force full sync
|
||||||
Sanity check failed. Please copy and paste the text below:\n%s\n%s""" % (c, s))
|
self.col.rollback()
|
||||||
|
self.col.modSchema()
|
||||||
|
self.col.save()
|
||||||
|
raise Exception("sanity check failed")
|
||||||
# finalize
|
# finalize
|
||||||
runHook("sync", "finalize")
|
runHook("sync", "finalize")
|
||||||
mod = self.server.finish()
|
mod = self.server.finish()
|
||||||
|
@ -179,19 +182,22 @@ Sanity check failed. Please copy and paste the text below:\n%s\n%s""" % (c, s))
|
||||||
self.prepareToChunk()
|
self.prepareToChunk()
|
||||||
|
|
||||||
def sanityCheck(self):
|
def sanityCheck(self):
|
||||||
# some basic checks to ensure the sync went ok. this is slow, so will
|
if self.col.db.scalar("""
|
||||||
# be removed before official release
|
select count() from cards where nid not in (select id from notes)"""):
|
||||||
assert not self.col.db.scalar("""
|
return "missing notes"
|
||||||
select count() from cards where nid not in (select id from notes)""")
|
if self.col.db.scalar("""
|
||||||
assert not self.col.db.scalar("""
|
select count() from notes where id not in (select distinct nid from cards)"""):
|
||||||
select count() from notes where id not in (select distinct nid from cards)""")
|
return "missing cards"
|
||||||
for t in "cards", "notes", "revlog", "graves":
|
for t in "cards", "notes", "revlog", "graves":
|
||||||
assert not self.col.db.scalar(
|
if self.col.db.scalar(
|
||||||
"select count() from %s where usn = -1" % t)
|
"select count() from %s where usn = -1" % t):
|
||||||
|
return "%t had usn = -1" % t
|
||||||
for g in self.col.decks.all():
|
for g in self.col.decks.all():
|
||||||
assert g['usn'] != -1
|
if g['usn'] == -1:
|
||||||
|
return "deck had usn = -1"
|
||||||
for t, usn in self.col.tags.allItems():
|
for t, usn in self.col.tags.allItems():
|
||||||
assert usn != -1
|
if usn == -1:
|
||||||
|
return "tag had usn = -1"
|
||||||
found = False
|
found = False
|
||||||
for m in self.col.models.all():
|
for m in self.col.models.all():
|
||||||
if self.col.server:
|
if self.col.server:
|
||||||
|
@ -200,7 +206,8 @@ select count() from notes where id not in (select distinct nid from cards)""")
|
||||||
m['usn'] = 0
|
m['usn'] = 0
|
||||||
found = True
|
found = True
|
||||||
else:
|
else:
|
||||||
assert m['usn'] != -1
|
if m['usn'] == -1:
|
||||||
|
return "model had usn = -1"
|
||||||
if found:
|
if found:
|
||||||
self.col.models.save()
|
self.col.models.save()
|
||||||
self.col.sched.reset()
|
self.col.sched.reset()
|
||||||
|
@ -218,6 +225,12 @@ select count() from notes where id not in (select distinct nid from cards)""")
|
||||||
len(self.col.decks.allConf()),
|
len(self.col.decks.allConf()),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def sanityCheck2(self, client):
|
||||||
|
server = self.sanityCheck()
|
||||||
|
if client != server:
|
||||||
|
return dict(status="bad", c=client, s=server)
|
||||||
|
return dict(status="ok")
|
||||||
|
|
||||||
def usnLim(self):
|
def usnLim(self):
|
||||||
if self.col.server:
|
if self.col.server:
|
||||||
return "usn >= %d" % self.minUsn
|
return "usn >= %d" % self.minUsn
|
||||||
|
@ -580,8 +593,8 @@ class RemoteServer(HttpSyncer):
|
||||||
def applyChunk(self, **kw):
|
def applyChunk(self, **kw):
|
||||||
return self._run("applyChunk", kw)
|
return self._run("applyChunk", kw)
|
||||||
|
|
||||||
def sanityCheck(self, **kw):
|
def sanityCheck2(self, **kw):
|
||||||
return self._run("sanityCheck", kw)
|
return self._run("sanityCheck2", kw)
|
||||||
|
|
||||||
def finish(self, **kw):
|
def finish(self, **kw):
|
||||||
return self._run("finish", kw)
|
return self._run("finish", kw)
|
||||||
|
|
|
@ -134,6 +134,10 @@ AnkiWeb is too busy at the moment. Please try again in a few minutes.""")
|
||||||
"Antivirus or firewall software is preventing Anki from connecting to the internet.")
|
"Antivirus or firewall software is preventing Anki from connecting to the internet.")
|
||||||
elif "407" in err:
|
elif "407" in err:
|
||||||
return _("Proxy authentication required.")
|
return _("Proxy authentication required.")
|
||||||
|
elif "sanity check failed" in err:
|
||||||
|
return _("After syncing, the collection was in an inconsistent \
|
||||||
|
state. To fix this problem, Anki will force a full sync. Please sync again, and \
|
||||||
|
choose which side you would like to keep.")
|
||||||
return err
|
return err
|
||||||
|
|
||||||
def _getUserPass(self):
|
def _getUserPass(self):
|
||||||
|
|
Loading…
Reference in a new issue