From 8d0a6cf97f263e0735b3002466328285e5a37454 Mon Sep 17 00:00:00 2001 From: ccd0 Date: Sun, 10 Dec 2017 21:47:28 -0800 Subject: [PATCH] Better protection against race conditions that can lead to data loss in DataBoard. --- src/Monitoring/ThreadWatcher.coffee | 5 +-- src/classes/DataBoard.coffee | 65 ++++++++++++++++++----------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/Monitoring/ThreadWatcher.coffee b/src/Monitoring/ThreadWatcher.coffee index 144a8a34b..a638f87bb 100644 --- a/src/Monitoring/ThreadWatcher.coffee +++ b/src/Monitoring/ThreadWatcher.coffee @@ -185,9 +185,8 @@ ThreadWatcher = interval = if ThreadWatcher.unreadEnabled and Conf['Show Unread Count'] then 5 * $.MINUTE else 2 * $.HOUR now = Date.now() unless now - interval < (db.data.lastChecked or 0) <= now - ThreadWatcher.fetchAllStatus() # calls forceSync - db.data.lastChecked = now - db.save() + ThreadWatcher.fetchAllStatus() + db.setLastChecked() ThreadWatcher.timeout = setTimeout ThreadWatcher.fetchAuto, interval buttonFetchAll: -> diff --git a/src/classes/DataBoard.coffee b/src/classes/DataBoard.coffee index 0654933aa..12c7ab3f2 100644 --- a/src/classes/DataBoard.coffee +++ b/src/classes/DataBoard.coffee @@ -13,21 +13,34 @@ class DataBoard @sync = sync $.on d, '4chanXInitFinished', init - save: (cb) -> $.set @key, @data, cb + changes: [] + + save: (change, cb) -> + snapshot1 = JSON.stringify @data + change() + {changes} = @ + changes.push change + $.get @key, {boards: {}}, (items) => + @data = items[@key] + snapshot2 = JSON.stringify @data + c() for c in changes + $.set @key, @data, => + @changes = [] + @sync?() if snapshot1 isnt snapshot2 + cb?() delete: ({boardID, threadID, postID}) -> - $.forceSync @key - if postID - return unless @data.boards[boardID]?[threadID] - delete @data.boards[boardID][threadID][postID] - @deleteIfEmpty {boardID, threadID} - else if threadID - return unless @data.boards[boardID] - delete @data.boards[boardID][threadID] - @deleteIfEmpty {boardID} - else - delete @data.boards[boardID] - @save() + @save => + if postID + return unless @data.boards[boardID]?[threadID] + delete @data.boards[boardID][threadID][postID] + @deleteIfEmpty {boardID, threadID} + else if threadID + return unless @data.boards[boardID] + delete @data.boards[boardID][threadID] + @deleteIfEmpty {boardID} + else + delete @data.boards[boardID] deleteIfEmpty: ({boardID, threadID}) -> if threadID @@ -38,24 +51,29 @@ class DataBoard delete @data.boards[boardID] set: (data, cb) -> - $.forceSync @key - @setUnsafe data, cb + @save => + @setUnsafe data + , cb - setUnsafe: ({boardID, threadID, postID, val}, cb) -> + setUnsafe: ({boardID, threadID, postID, val}) -> if postID isnt undefined ((@data.boards[boardID] or= {})[threadID] or= {})[postID] = val else if threadID isnt undefined (@data.boards[boardID] or= {})[threadID] = val else @data.boards[boardID] = val - @save cb extend: ({boardID, threadID, postID, val, rm}, cb) -> - $.forceSync @key - oldVal = @get {boardID, threadID, postID, val: {}} - delete oldVal[key] for key in rm or [] - $.extend oldVal, val - @setUnsafe {boardID, threadID, postID, val: oldVal}, cb + @save => + oldVal = @get {boardID, threadID, postID, val: {}} + delete oldVal[key] for key in rm or [] + $.extend oldVal, val + @setUnsafe {boardID, threadID, postID, val: oldVal} + , cb + + setLastChecked: -> + @save => + @data.lastChecked = Date.now() get: ({boardID, threadID, postID, defaultValue}) -> if board = @data.boards[boardID] @@ -78,7 +96,6 @@ class DataBoard $.forceSync @key clean: -> - $.forceSync @key for boardID, val of @data.boards @deleteIfEmpty {boardID} @@ -109,7 +126,7 @@ class DataBoard threads[ID] = board[ID] if ID of board @data.boards[boardID] = threads @deleteIfEmpty {boardID} - @save() + $.set @key, @data onSync: (data) => @data = data or boards: {}