variable swarm polling based on activeAt timestamp

pull/1723/head
Audric Ackermann 4 years ago
parent cc6951e4c2
commit 7ea30b70ca
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -450,11 +450,7 @@
}, window.CONSTANTS.NOTIFICATION_ENABLE_TIMEOUT_SECONDS * 1000); }, window.CONSTANTS.NOTIFICATION_ENABLE_TIMEOUT_SECONDS * 1000);
window.NewReceiver.queueAllCached(); window.NewReceiver.queueAllCached();
window
.getSwarmPollingInstance()
.addPubkey(window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache());
window.getSwarmPollingInstance().start();
window.libsession.Utils.AttachmentDownloads.start({ window.libsession.Utils.AttachmentDownloads.start({
logger: window.log, logger: window.log,
}); });

@ -245,8 +245,6 @@ const doAppStartUp = () => {
debounce(triggerAvatarReUploadIfNeeded, 200); debounce(triggerAvatarReUploadIfNeeded, 200);
// TODO: Investigate the case where we reconnect // TODO: Investigate the case where we reconnect
const ourKey = UserUtils.getOurPubKeyStrFromCache();
getSwarmPollingInstance().addPubkey(ourKey);
getSwarmPollingInstance().start(); getSwarmPollingInstance().start();
}; };

@ -44,6 +44,7 @@ import { NotificationForConvoOption } from '../components/conversation/Conversat
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { updateConfirmModal } from '../state/ducks/modalDialog'; import { updateConfirmModal } from '../state/ducks/modalDialog';
import { createTaskWithTimeout } from '../session/utils/TaskWithTimeout'; import { createTaskWithTimeout } from '../session/utils/TaskWithTimeout';
import { DURATION, SWARM_POLLING_TIMEOUT } from '../session/constants';
export enum ConversationTypeEnum { export enum ConversationTypeEnum {
GROUP = 'group', GROUP = 'group',

@ -17,6 +17,12 @@ export const TTL_DEFAULT = {
TTL_MAX: 14 * DURATION.DAYS, TTL_MAX: 14 * DURATION.DAYS,
}; };
export const SWARM_POLLING_TIMEOUT = {
ACTIVE: DURATION.SECONDS * 5,
MEDIUM_ACTIVE: DURATION.SECONDS * 60,
INACTIVE: DURATION.MINUTES * 60,
};
export const PROTOCOLS = { export const PROTOCOLS = {
// tslint:disable-next-line: no-http-string // tslint:disable-next-line: no-http-string
HTTP: 'http:', HTTP: 'http:',

@ -12,9 +12,11 @@ import {
updateLastHash, updateLastHash,
} from '../../../ts/data/data'; } from '../../../ts/data/data';
import { StringUtils } from '../../session/utils'; import { StringUtils, UserUtils } from '../../session/utils';
import { getConversationController } from '../conversations'; import { getConversationController } from '../conversations';
import { ConversationModel } from '../../models/conversation'; import { ConversationModel } from '../../models/conversation';
import { DURATION, SWARM_POLLING_TIMEOUT } from '../constants';
import { ConversationController } from '../conversations/ConversationController';
type PubkeyToHash = { [key: string]: string }; type PubkeyToHash = { [key: string]: string };
@ -49,33 +51,26 @@ export const getSwarmPollingInstance = () => {
return instance; return instance;
}; };
export class SwarmPolling { class SwarmPolling {
private pubkeys: Array<PubKey>; private ourPubkey: PubKey | undefined;
private groupPubkeys: Array<PubKey>; private groupPolling: Array<{ pubkey: PubKey; lastPolledTimestamp: number }>;
private readonly lastHashes: { [key: string]: PubkeyToHash }; private readonly lastHashes: { [key: string]: PubkeyToHash };
constructor() { constructor() {
this.pubkeys = []; this.groupPolling = [];
this.groupPubkeys = [];
this.lastHashes = {}; this.lastHashes = {};
} }
public start(): void { public start(): void {
this.ourPubkey = UserUtils.getOurPubKeyFromCache();
this.loadGroupIds(); this.loadGroupIds();
void this.pollForAllKeys(); void this.pollForAllKeys();
} }
public addGroupId(pubkey: PubKey) { public addGroupId(pubkey: PubKey) {
if (this.groupPubkeys.findIndex(m => m.key === pubkey.key) === -1) { if (this.groupPolling.findIndex(m => m.pubkey.key === pubkey.key) === -1) {
window?.log?.info('Swarm addGroupId: adding pubkey to polling', pubkey.key); window?.log?.info('Swarm addGroupId: adding pubkey to polling', pubkey.key);
this.groupPubkeys.push(pubkey); this.groupPolling.push({ pubkey, lastPolledTimestamp: 0 });
}
}
public addPubkey(pk: PubKey | string) {
const pubkey = PubKey.cast(pk);
if (this.pubkeys.findIndex(m => m.key === pubkey.key) === -1) {
this.pubkeys.push(pubkey);
} }
} }
@ -83,11 +78,13 @@ export class SwarmPolling {
const pubkey = PubKey.cast(pk); const pubkey = PubKey.cast(pk);
window?.log?.info('Swarm removePubkey: removing pubkey from polling', pubkey.key); window?.log?.info('Swarm removePubkey: removing pubkey from polling', pubkey.key);
this.pubkeys = this.pubkeys.filter(key => !pubkey.isEqual(key)); if (this.ourPubkey && PubKey.cast(pk).isEqual(this.ourPubkey)) {
this.groupPubkeys = this.groupPubkeys.filter(key => !pubkey.isEqual(key)); this.ourPubkey = undefined;
}
this.groupPolling = this.groupPolling.filter(group => !pubkey.isEqual(group.pubkey));
} }
protected async pollOnceForKey(pubkey: PubKey, isGroup: boolean) { private async pollOnceForKey(pubkey: PubKey, isGroup: boolean) {
// NOTE: sometimes pubkey is string, sometimes it is object, so // NOTE: sometimes pubkey is string, sometimes it is object, so
// accept both until this is fixed: // accept both until this is fixed:
const pkStr = pubkey.key; const pkStr = pubkey.key;
@ -123,6 +120,19 @@ export class SwarmPolling {
// Merge results into one list of unique messages // Merge results into one list of unique messages
const messages = _.uniqBy(_.flatten(results), (x: any) => x.hash); const messages = _.uniqBy(_.flatten(results), (x: any) => x.hash);
if (isGroup) {
// update the last fetched timestamp
this.groupPolling = this.groupPolling.map(group => {
if (PubKey.isEqual(pubkey, group.pubkey)) {
return {
...group,
lastPolledTimestamp: Date.now(),
};
}
return group;
});
}
const newMessages = await this.handleSeenMessages(messages); const newMessages = await this.handleSeenMessages(messages);
newMessages.forEach((m: Message) => { newMessages.forEach((m: Message) => {
@ -133,7 +143,7 @@ export class SwarmPolling {
// Fetches messages for `pubkey` from `node` potentially updating // Fetches messages for `pubkey` from `node` potentially updating
// the lash hash record // the lash hash record
protected async pollNodeForKey(node: Snode, pubkey: PubKey): Promise<Array<any>> { private async pollNodeForKey(node: Snode, pubkey: PubKey): Promise<Array<any>> {
const edkey = node.pubkey_ed25519; const edkey = node.pubkey_ed25519;
const pkStr = pubkey.key; const pkStr = pubkey.key;
@ -188,21 +198,72 @@ export class SwarmPolling {
return newMessages; return newMessages;
} }
/**
* As of today, we pull closed group pubkeys as follow:
* if activeAt is not set, poll only once per hour
* if activeAt is less than an hour old, poll every 5 seconds or so
* if activeAt is less than a day old, poll every minutes only.
* If activeAt is more than a day old, poll only once per hour
*/
private getPollingTimeout(convoId: PubKey) {
const convo = getConversationController().get(convoId.key);
if (!convo) {
return this.pollOnceForKey(convoId, true);
}
const activeAt = convo.get('active_at');
if (!activeAt) {
return SWARM_POLLING_TIMEOUT.INACTIVE;
}
const currentTimestamp = Date.now();
// consider that this is an active group if activeAt is less than an hour old
if (currentTimestamp - activeAt <= DURATION.HOURS * 1) {
return SWARM_POLLING_TIMEOUT.ACTIVE;
}
if (currentTimestamp - activeAt <= DURATION.DAYS * 1) {
return SWARM_POLLING_TIMEOUT.MEDIUM_ACTIVE;
}
return SWARM_POLLING_TIMEOUT.INACTIVE;
}
private async pollForAllKeys() { private async pollForAllKeys() {
const directPromises = this.pubkeys.map(async pk => { // we always poll as often as possible for our pubkey
return this.pollOnceForKey(pk, false); const directPromise = this.ourPubkey
}); ? this.pollOnceForKey(this.ourPubkey, false)
: Promise.resolve();
const now = Date.now();
const groupPromises = this.groupPolling.map(async group => {
const convoPollingTimeout = this.getPollingTimeout(group.pubkey);
const diff = now - group.lastPolledTimestamp;
const loggingId = getConversationController()
.get(group.pubkey.key)
.idForLogging();
if (diff >= convoPollingTimeout) {
window?.log?.info(
`Polling for ${loggingId}; timeout: ${convoPollingTimeout} ; diff: ${diff}`
);
return this.pollOnceForKey(group.pubkey, true);
}
window?.log?.info(
`Not polling for ${loggingId}; timeout: ${convoPollingTimeout} ; diff: ${diff}`
);
const groupPromises = this.groupPubkeys.map(async pk => { return Promise.resolve();
return this.pollOnceForKey(pk, true);
}); });
try { try {
await Promise.all(_.concat(directPromises, groupPromises)); await Promise.all(_.concat(directPromise, groupPromises));
} catch (e) { } catch (e) {
window?.log?.warn('pollForAllKeys swallowing exception: ', e); window?.log?.warn('pollForAllKeys swallowing exception: ', e);
throw e; throw e;
} finally { } finally {
setTimeout(this.pollForAllKeys.bind(this), 2000); setTimeout(this.pollForAllKeys.bind(this), SWARM_POLLING_TIMEOUT.ACTIVE);
} }
} }

@ -152,6 +152,10 @@ export class PubKey {
return key.replace(PubKey.PREFIX_GROUP_TEXTSECURE, ''); return key.replace(PubKey.PREFIX_GROUP_TEXTSECURE, '');
} }
public static isEqual(comparator1: PubKey | string, comparator2: PubKey | string) {
return PubKey.cast(comparator1).isEqual(comparator2);
}
public isEqual(comparator: PubKey | string) { public isEqual(comparator: PubKey | string) {
return comparator instanceof PubKey return comparator instanceof PubKey
? this.key === comparator.key ? this.key === comparator.key

@ -33,7 +33,6 @@ const defaultRoomsSlice = createSlice({
}, },
updateDefaultRoomsInProgress(state, action) { updateDefaultRoomsInProgress(state, action) {
const inProgress = action.payload as boolean; const inProgress = action.payload as boolean;
window?.log?.info('fetching default rooms inProgress?', action.payload);
return { ...state, inProgress }; return { ...state, inProgress };
}, },
updateDefaultBase64RoomData(state, action: PayloadAction<Base64Update>) { updateDefaultBase64RoomData(state, action: PayloadAction<Base64Update>) {

Loading…
Cancel
Save