Skip to content

Commit

Permalink
Merge pull request #967 from girder/event-stream-fallback
Browse files Browse the repository at this point in the history
Add a polling fallback for when the server event stream is off
  • Loading branch information
manthey authored Sep 22, 2022
2 parents 1ac259b + 478b4b9 commit c5d7942
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## 1.17.1

### Improvements
- Fallback when server notification streams are turned off ([#967](../../pull/967))

## 1.17.0

### Features
Expand Down
2 changes: 2 additions & 0 deletions girder/girder_large_image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ def handleFinalizeUploadBefore(event):
@setting_utilities.validator({
constants.PluginSettings.LARGE_IMAGE_SHOW_THUMBNAILS,
constants.PluginSettings.LARGE_IMAGE_SHOW_VIEWER,
constants.PluginSettings.LARGE_IMAGE_NOTIFICATION_STREAM_FALLBACK,
})
def validateBoolean(doc):
val = doc['value']
Expand Down Expand Up @@ -335,6 +336,7 @@ def validateFolder(doc):
constants.PluginSettings.LARGE_IMAGE_AUTO_SET: True,
constants.PluginSettings.LARGE_IMAGE_MAX_THUMBNAIL_FILES: 10,
constants.PluginSettings.LARGE_IMAGE_MAX_SMALL_IMAGE_SIZE: 4096,
constants.PluginSettings.LARGE_IMAGE_NOTIFICATION_STREAM_FALLBACK: True,
})


Expand Down
1 change: 1 addition & 0 deletions girder/girder_large_image/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ class PluginSettings:
LARGE_IMAGE_MAX_SMALL_IMAGE_SIZE = 'large_image.max_small_image_size'
LARGE_IMAGE_AUTO_USE_ALL_FILES = 'large_image.auto_use_all_files'
LARGE_IMAGE_CONFIG_FOLDER = 'large_image.config_folder'
LARGE_IMAGE_NOTIFICATION_STREAM_FALLBACK = 'large_image.notification_stream_fallback'
82 changes: 82 additions & 0 deletions girder/girder_large_image/web_client/eventStream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* This implements a polling fallback if event streams are disabled on the
* server. */

import eventStream from '@girder/core/utilities/EventStream';
import { restRequest } from '@girder/core/rest';

import largeImageConfig from './views/configView';

eventStream.on('g:eventStream.disable', () => {
largeImageConfig.getSettings(() => {
if (largeImageConfig.settings['large_image.notification_stream_fallback'] === false) {
return;
}
const MIN_TIMEOUT = 5000;
const MAX_TIMEOUT = 60000;
const TIMEOUT_FALLOFF = 1000;
let pollCallback;
let timeout = MIN_TIMEOUT;
let lastTimestamp;
try {
lastTimestamp = window.localStorage.getItem('sseFallbackTimestamp');
if (lastTimestamp === 'null') {
lastTimestamp = undefined;
}
} catch (e) {
// Ignore any errors raised by localStorage
}

function checkNotifications() {
pollCallback = null;
restRequest({
url: 'notification',
data: {
since: lastTimestamp || undefined
},
error: null
}).done((resp) => {
if (!resp.length) {
if (timeout < MAX_TIMEOUT) {
timeout += TIMEOUT_FALLOFF;
}
} else {
timeout = MIN_TIMEOUT;
}
resp.forEach((obj) => {
lastTimestamp = obj.time || lastTimestamp;
try {
eventStream.trigger('g:event.' + obj.type, obj);
} catch (e) {
// ignore errors
}
});
try {
window.localStorage.setItem('sseFallbackTimestamp', lastTimestamp);
} catch (e) {
// Ignore any errors raised by localStorage
}
pollCallback = window.setTimeout(checkNotifications, timeout);
}).fail(() => {
if (timeout < MAX_TIMEOUT) {
timeout += TIMEOUT_FALLOFF;
}
pollCallback = window.setTimeout(checkNotifications, timeout);
});
}

document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
timeout = MIN_TIMEOUT;
if (!pollCallback) {
pollCallback = window.setTimeout(checkNotifications, timeout);
eventStream.trigger('g:eventStream.start');
}
} else if (pollCallback) {
window.clearTimeout(pollCallback);
eventStream.trigger('g:eventStream.stop');
pollCallback = null;
}
});
pollCallback = window.setTimeout(checkNotifications, timeout);
});
});
1 change: 1 addition & 0 deletions girder/girder_large_image/web_client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { registerPluginNamespace } from '@girder/core/pluginUtils';

// import modules for side effects
import './routes';
import './eventStream';
import './views/fileList';
import './views/itemList';
import './views/itemView';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,20 @@ form#g-large-image-form(role="form")
.input-group-btn
button.g-open-browser.btn.btn-default(type="button")
i.icon-folder-open
.form-group
label Server Notification Stream Fallback
p.g-large-image-description
| If the Server Notification Stream is turned off, should polling be used
| instead? If neither is used, some web pages will need to be manually
| refreshed to see updates. If this is changed and the notification
| streams are off, existing web pages will need to be reloaded once for
| the change to take effect.
.g-large-image-stream-fallback-container
label.radio-inline
input.g-large-image-stream-fallback(type="radio", name="g-large-image-stream", checked=settings['large_image.notification_stream_fallback'] !== false ? 'checked': undefined)
| Polling fallback
label.radio-inline
input.g-large-image-stream-no-fallback(type="radio", name="g-large-image-stream", checked=settings['large_image.notification_stream_fallback'] !== false ? undefined : 'checked')
| No polling
p#g-large-image-error-message.g-validation-failed-message
input.btn.btn-sm.btn-primary(type="submit", value="Save")
3 changes: 3 additions & 0 deletions girder/girder_large_image/web_client/views/configView.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ var ConfigView = View.extend({
}, {
key: 'large_image.config_folder',
value: (this.$('#g-large-image-config-folder').val() || '').split(' ')[0]
}, {
key: 'large_image.notification_stream_fallback',
value: this.$('.g-large-image-stream-fallback').prop('checked')
}]);
},
'click .g-open-browser': '_openBrowser'
Expand Down
16 changes: 16 additions & 0 deletions girder/test_girder/test_web_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,19 @@ def testWebClient(boundServer, fsAssetstore, db, spec, girderWorker):
def testWebClientNoWorker(boundServer, fsAssetstore, db, spec):
spec = os.path.join(os.path.dirname(__file__), 'web_client_specs', spec)
runWebClientTest(boundServer, spec, 15000)


@pytest.mark.singular
@pytest.mark.usefixtures('unbindLargeImage')
@pytest.mark.plugin('large_image')
@pytest.mark.parametrize('spec', (
'imageViewerSpec.js',
))
def testWebClientNoStream(boundServer, fsAssetstore, db, spec, girderWorker):
from girder.models.setting import Setting
from girder.settings import SettingKey

Setting().set(SettingKey.ENABLE_NOTIFICATION_STREAM, False)

spec = os.path.join(os.path.dirname(__file__), 'web_client_specs', spec)
runWebClientTest(boundServer, spec, 15000)

0 comments on commit c5d7942

Please sign in to comment.