Self-repairing message counter
This commit is contained in:
parent
5780c3d4b8
commit
3f7957c20d
8 changed files with 81 additions and 5 deletions
libtextsecure/test
test
ts
|
@ -77,4 +77,6 @@ before(async () => {
|
||||||
err && err.stack ? err.stack : err
|
err && err.stack ? err.stack : err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await window.Signal.Util.initializeMessageCounter();
|
||||||
});
|
});
|
||||||
|
|
|
@ -88,6 +88,7 @@ before(async () => {
|
||||||
err && err.stack ? err.stack : err
|
err && err.stack ? err.stack : err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
await window.Signal.Util.initializeMessageCounter();
|
||||||
await window.Signal.Data.removeAll();
|
await window.Signal.Data.removeAll();
|
||||||
await window.storage.fetch();
|
await window.storage.fetch();
|
||||||
});
|
});
|
||||||
|
|
|
@ -139,6 +139,8 @@ export async function startApp(): Promise<void> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await window.Signal.Util.initializeMessageCounter();
|
||||||
|
|
||||||
// Initialize WebAPI as early as possible
|
// Initialize WebAPI as early as possible
|
||||||
let server: WebAPIType | undefined;
|
let server: WebAPIType | undefined;
|
||||||
let messageReceiver: MessageReceiver | undefined;
|
let messageReceiver: MessageReceiver | undefined;
|
||||||
|
|
|
@ -277,6 +277,8 @@ const dataInterface: ClientInterface = {
|
||||||
processGroupCallRingCancelation,
|
processGroupCallRingCancelation,
|
||||||
cleanExpiredGroupCallRings,
|
cleanExpiredGroupCallRings,
|
||||||
|
|
||||||
|
getMaxMessageCounter,
|
||||||
|
|
||||||
getStatisticsForLogging,
|
getStatisticsForLogging,
|
||||||
|
|
||||||
// Test-only
|
// Test-only
|
||||||
|
@ -1656,6 +1658,10 @@ async function updateAllConversationColors(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMaxMessageCounter(): Promise<number | undefined> {
|
||||||
|
return channels.getMaxMessageCounter();
|
||||||
|
}
|
||||||
|
|
||||||
function getStatisticsForLogging(): Promise<Record<string, string>> {
|
function getStatisticsForLogging(): Promise<Record<string, string>> {
|
||||||
return channels.getStatisticsForLogging();
|
return channels.getStatisticsForLogging();
|
||||||
}
|
}
|
||||||
|
|
|
@ -486,6 +486,7 @@ export type DataInterface = {
|
||||||
}
|
}
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
|
|
||||||
|
getMaxMessageCounter(): Promise<number | undefined>;
|
||||||
getStatisticsForLogging(): Promise<Record<string, string>>;
|
getStatisticsForLogging(): Promise<Record<string, string>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -268,6 +268,8 @@ const dataInterface: ServerInterface = {
|
||||||
processGroupCallRingCancelation,
|
processGroupCallRingCancelation,
|
||||||
cleanExpiredGroupCallRings,
|
cleanExpiredGroupCallRings,
|
||||||
|
|
||||||
|
getMaxMessageCounter,
|
||||||
|
|
||||||
getStatisticsForLogging,
|
getStatisticsForLogging,
|
||||||
|
|
||||||
// Server-only
|
// Server-only
|
||||||
|
@ -6493,6 +6495,25 @@ async function cleanExpiredGroupCallRings(): Promise<void> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getMaxMessageCounter(): Promise<number | undefined> {
|
||||||
|
const db = getInstance();
|
||||||
|
|
||||||
|
return db
|
||||||
|
.prepare<EmptyQuery>(
|
||||||
|
`
|
||||||
|
SELECT MAX(counter)
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT MAX(received_at) AS counter FROM messages
|
||||||
|
UNION
|
||||||
|
SELECT MAX(timestamp) AS counter FROM unprocessed
|
||||||
|
)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.pluck()
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
async function getStatisticsForLogging(): Promise<Record<string, string>> {
|
async function getStatisticsForLogging(): Promise<Record<string, string>> {
|
||||||
const counts = await pProps({
|
const counts = await pProps({
|
||||||
messageCount: getMessageCount(),
|
messageCount: getMessageCount(),
|
||||||
|
|
|
@ -1,15 +1,56 @@
|
||||||
// Copyright 2021 Signal Messenger, LLC
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { debounce } from 'lodash';
|
import { debounce, isNumber } from 'lodash';
|
||||||
|
|
||||||
|
import { strictAssert } from './assert';
|
||||||
|
import Data from '../sql/Client';
|
||||||
|
|
||||||
let receivedAtCounter: number | undefined;
|
let receivedAtCounter: number | undefined;
|
||||||
|
|
||||||
export function incrementMessageCounter(): number {
|
export async function initializeMessageCounter(): Promise<void> {
|
||||||
if (!receivedAtCounter) {
|
strictAssert(
|
||||||
receivedAtCounter =
|
receivedAtCounter === undefined,
|
||||||
Number(localStorage.getItem('lastReceivedAtCounter')) || Date.now();
|
'incrementMessageCounter: already initialized'
|
||||||
|
);
|
||||||
|
|
||||||
|
const storedCounter = Number(localStorage.getItem('lastReceivedAtCounter'));
|
||||||
|
const dbCounter = await Data.getMaxMessageCounter();
|
||||||
|
|
||||||
|
if (isNumber(dbCounter) && isNumber(storedCounter)) {
|
||||||
|
window.log.info(
|
||||||
|
'initializeMessageCounter: picking max of db/stored counters'
|
||||||
|
);
|
||||||
|
receivedAtCounter = Math.max(dbCounter, storedCounter);
|
||||||
|
|
||||||
|
if (receivedAtCounter !== storedCounter) {
|
||||||
|
window.log.warn(
|
||||||
|
'initializeMessageCounter: mismatch between db/stored counters'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
} else if (isNumber(storedCounter)) {
|
||||||
|
window.log.info('initializeMessageCounter: picking stored counter');
|
||||||
|
receivedAtCounter = storedCounter;
|
||||||
|
} else if (isNumber(dbCounter)) {
|
||||||
|
window.log.info(
|
||||||
|
'initializeMessageCounter: picking fallback counter from the database'
|
||||||
|
);
|
||||||
|
receivedAtCounter = dbCounter;
|
||||||
|
} else {
|
||||||
|
window.log.info('initializeMessageCounter: defaulting to Date.now()');
|
||||||
|
receivedAtCounter = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedCounter !== receivedAtCounter) {
|
||||||
|
localStorage.setItem('lastReceivedAtCounter', String(receivedAtCounter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function incrementMessageCounter(): number {
|
||||||
|
strictAssert(
|
||||||
|
receivedAtCounter !== undefined,
|
||||||
|
'incrementMessageCounter: not initialized'
|
||||||
|
);
|
||||||
|
|
||||||
receivedAtCounter += 1;
|
receivedAtCounter += 1;
|
||||||
debouncedUpdateLastReceivedAt();
|
debouncedUpdateLastReceivedAt();
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { getTextWithMentions } from './getTextWithMentions';
|
||||||
import { getUserAgent } from './getUserAgent';
|
import { getUserAgent } from './getUserAgent';
|
||||||
import { hasExpired } from './hasExpired';
|
import { hasExpired } from './hasExpired';
|
||||||
import {
|
import {
|
||||||
|
initializeMessageCounter,
|
||||||
incrementMessageCounter,
|
incrementMessageCounter,
|
||||||
flushMessageCounter,
|
flushMessageCounter,
|
||||||
} from './incrementMessageCounter';
|
} from './incrementMessageCounter';
|
||||||
|
@ -61,6 +62,7 @@ export {
|
||||||
getUserAgent,
|
getUserAgent,
|
||||||
hasExpired,
|
hasExpired,
|
||||||
incrementMessageCounter,
|
incrementMessageCounter,
|
||||||
|
initializeMessageCounter,
|
||||||
isFileDangerous,
|
isFileDangerous,
|
||||||
longRunningTaskWrapper,
|
longRunningTaskWrapper,
|
||||||
makeLookup,
|
makeLookup,
|
||||||
|
|
Loading…
Add table
Reference in a new issue