@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Conpherence - kill race conditions around update

Summary:
Fixes T6713. The idea is to keep checking what's going on in the update paths that touch the DOM. If we're doing an update or should be doing a different update, then we bail early.

This is the type of code + testing that makes me dizzy after awhile, but I think it works...

Test Plan:
added a "forceStall" parameter to the column view controller, which when specified sleeps for seconds before returning. I then augmented the JS such that the "send message" code for the durable column would specifiy this parameter.

For actual testing, I then spammed the heck out of the durable column channel and saw each message only once. I also spammed the column, switched browsers to a user on the same thread in the normal "speedy" view, sent messages there, and also only received one copy

Reviewers: chad, epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T6713

Differential Revision: https://secure.phabricator.com/D12092

+52 -19
+12 -12
resources/celerity/map.php
··· 352 352 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => 'ea681761', 353 353 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 354 354 'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', 355 - 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'cff1902b', 355 + 'rsrc/js/application/conpherence/ConpherenceThreadManager.js' => 'bbc850a4', 356 356 'rsrc/js/application/conpherence/behavior-durable-column.js' => 'e975bd12', 357 357 'rsrc/js/application/conpherence/behavior-menu.js' => 'c4151295', 358 358 'rsrc/js/application/conpherence/behavior-pontificate.js' => '21ba5861', ··· 517 517 'conpherence-menu-css' => 'c6ac5299', 518 518 'conpherence-message-pane-css' => '2526107d', 519 519 'conpherence-notification-css' => '04a6e10a', 520 - 'conpherence-thread-manager' => 'cff1902b', 520 + 'conpherence-thread-manager' => 'bbc850a4', 521 521 'conpherence-update-css' => '1099a660', 522 522 'conpherence-widget-pane-css' => '3d575438', 523 523 'differential-changeset-view-css' => '6a8b172a', ··· 1684 1684 'javelin-stratcom', 1685 1685 'javelin-dom', 1686 1686 ), 1687 + 'bbc850a4' => array( 1688 + 'javelin-dom', 1689 + 'javelin-util', 1690 + 'javelin-stratcom', 1691 + 'javelin-install', 1692 + 'javelin-workflow', 1693 + 'javelin-router', 1694 + 'javelin-behavior-device', 1695 + 'javelin-vector', 1696 + ), 1687 1697 'bbdf75ca' => array( 1688 1698 'javelin-behavior', 1689 1699 'javelin-dom', ··· 1763 1773 'javelin-dom', 1764 1774 'javelin-stratcom', 1765 1775 'phabricator-phtize', 1766 - ), 1767 - 'cff1902b' => array( 1768 - 'javelin-dom', 1769 - 'javelin-util', 1770 - 'javelin-stratcom', 1771 - 'javelin-install', 1772 - 'javelin-workflow', 1773 - 'javelin-router', 1774 - 'javelin-behavior-device', 1775 - 'javelin-vector', 1776 1776 ), 1777 1777 'd19198c8' => array( 1778 1778 'javelin-install',
+3
src/applications/conpherence/controller/ConpherenceViewController.php
··· 58 58 $content['title'] = $title; 59 59 60 60 if ($request->isAjax()) { 61 + $content['threadID'] = $conpherence->getID(); 62 + $content['threadPHID'] = $conpherence->getPHID(); 63 + $content['latestTransactionID'] = $data['latest_transaction_id']; 61 64 return id(new AphrontAjaxResponse())->setContent($content); 62 65 } 63 66
+37 -7
webroot/rsrc/js/application/conpherence/ConpherenceThreadManager.js
··· 167 167 })); 168 168 }, 169 169 170 + _shouldUpdateDOM: function(r) { 171 + if (this._updating && 172 + this._updating.threadPHID == this._loadedThreadPHID) { 173 + // we have a different, more current update in progress so 174 + // return early 175 + if (r.latest_transaction_id < this._updating.knownID) { 176 + return false; 177 + } 178 + // we need to let the update code handle things here 179 + if (r.latest_transaction_id > this._updating.knownID) { 180 + this._updating.knownID = r.latest_transaction_id; 181 + return false; 182 + } 183 + } 184 + return true; 185 + }, 186 + 170 187 _updateThread: function() { 171 188 var params = this._getParams({ 172 189 action: 'load', ··· 177 194 var workflow = new JX.Workflow(uri) 178 195 .setData(params) 179 196 .setHandler(JX.bind(this, function(r) { 197 + if (this._updating && 198 + this._updating.threadPHID == this._loadedThreadPHID) { 199 + // we have a different, more current update in progress so 200 + // return early 201 + if (r.latest_transaction_id < this._updating.knownID) { 202 + return; 203 + } 204 + } 180 205 this._latestTransactionID = r.latest_transaction_id; 206 + this._updating.knownID = r.latest_transaction_id; 181 207 this._didUpdateThreadCallback(r); 182 208 })); 183 209 ··· 191 217 }; 192 218 workflow.listen(stage, JX.bind(this, function() { 193 219 // TODO - do we need to handle if we switch threads somehow? 194 - var need_sync = (this._updating.knownID > this._latestTransactionID); 195 - this._updating = null; 220 + var need_sync = this._updating && 221 + (this._updating.knownID > this._latestTransactionID); 196 222 if (need_sync) { 197 - this._updateThread(); 223 + return this._updateThread(); 198 224 } 199 225 })); 200 226 workflow.start(); ··· 206 232 var workflow = new JX.Workflow.newFromLink(link) 207 233 .setData(params) 208 234 .setHandler(JX.bind(this, function(r) { 209 - this._latestTransactionID = r.latest_transaction_id; 210 - this._didUpdateWorkflowCallback(r); 235 + if (this._shouldUpdateDOM(r)) { 236 + this._latestTransactionID = r.latest_transaction_id; 237 + this._didUpdateWorkflowCallback(r); 238 + } 211 239 })); 212 240 this.syncWorkflow(workflow, params.stage); 213 241 }, ··· 249 277 250 278 var workflow = JX.Workflow.newFromForm(form, params, keep_enabled) 251 279 .setHandler(JX.bind(this, function(r) { 252 - this._latestTransactionID = r.latest_transaction_id; 253 - this._didSendMessageCallback(r); 280 + if (this._shouldUpdateDOM(r)) { 281 + this._latestTransactionID = r.latest_transaction_id; 282 + this._didSendMessageCallback(r); 283 + } 254 284 })); 255 285 this.syncWorkflow(workflow, 'finally'); 256 286