Fix handling of encrypted unprocessed envelopes
This commit is contained in:
parent
0d87e3e6c9
commit
5bdb39a95b
7 changed files with 73 additions and 23 deletions
|
@ -375,6 +375,11 @@ export type StickerPackRefType = Readonly<{
|
||||||
export type UnprocessedType = {
|
export type UnprocessedType = {
|
||||||
id: string;
|
id: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
/*
|
||||||
|
* A client generated date used for removing old envelopes from the table
|
||||||
|
* on startup.
|
||||||
|
*/
|
||||||
|
receivedAtDate: number;
|
||||||
receivedAtCounter: number;
|
receivedAtCounter: number;
|
||||||
attempts: number;
|
attempts: number;
|
||||||
type: number;
|
type: number;
|
||||||
|
|
|
@ -4629,6 +4629,7 @@ function saveUnprocessed(db: WritableDB, data: UnprocessedType): string {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
receivedAtDate,
|
||||||
receivedAtCounter,
|
receivedAtCounter,
|
||||||
attempts,
|
attempts,
|
||||||
type,
|
type,
|
||||||
|
@ -4659,6 +4660,7 @@ function saveUnprocessed(db: WritableDB, data: UnprocessedType): string {
|
||||||
id,
|
id,
|
||||||
timestamp,
|
timestamp,
|
||||||
receivedAtCounter,
|
receivedAtCounter,
|
||||||
|
receivedAtDate,
|
||||||
attempts,
|
attempts,
|
||||||
type,
|
type,
|
||||||
isEncrypted,
|
isEncrypted,
|
||||||
|
@ -4680,6 +4682,7 @@ function saveUnprocessed(db: WritableDB, data: UnprocessedType): string {
|
||||||
$id,
|
$id,
|
||||||
$timestamp,
|
$timestamp,
|
||||||
$receivedAtCounter,
|
$receivedAtCounter,
|
||||||
|
$receivedAtDate,
|
||||||
$attempts,
|
$attempts,
|
||||||
$type,
|
$type,
|
||||||
$isEncrypted,
|
$isEncrypted,
|
||||||
|
@ -4702,7 +4705,8 @@ function saveUnprocessed(db: WritableDB, data: UnprocessedType): string {
|
||||||
).run({
|
).run({
|
||||||
id,
|
id,
|
||||||
timestamp,
|
timestamp,
|
||||||
receivedAtCounter: receivedAtCounter ?? null,
|
receivedAtCounter,
|
||||||
|
receivedAtDate,
|
||||||
attempts,
|
attempts,
|
||||||
type,
|
type,
|
||||||
isEncrypted: isEncrypted ? 1 : 0,
|
isEncrypted: isEncrypted ? 1 : 0,
|
||||||
|
@ -4750,9 +4754,11 @@ function getAllUnprocessedIds(db: WritableDB): Array<string> {
|
||||||
return db.transaction(() => {
|
return db.transaction(() => {
|
||||||
// cleanup first
|
// cleanup first
|
||||||
const { changes: deletedStaleCount } = db
|
const { changes: deletedStaleCount } = db
|
||||||
.prepare<Query>('DELETE FROM unprocessed WHERE timestamp < $monthAgo')
|
.prepare<Query>(
|
||||||
|
'DELETE FROM unprocessed WHERE receivedAtDate < $messageQueueCutoff'
|
||||||
|
)
|
||||||
.run({
|
.run({
|
||||||
monthAgo: Date.now() - 45 * durations.DAY,
|
messageQueueCutoff: Date.now() - 45 * durations.DAY,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (deletedStaleCount !== 0) {
|
if (deletedStaleCount !== 0) {
|
||||||
|
|
38
ts/sql/migrations/1320-unprocessed-received-at-date.ts
Normal file
38
ts/sql/migrations/1320-unprocessed-received-at-date.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { LoggerType } from '../../types/Logging';
|
||||||
|
import { sql } from '../util';
|
||||||
|
import type { WritableDB } from '../Interface';
|
||||||
|
|
||||||
|
export const version = 1320;
|
||||||
|
|
||||||
|
export function updateToSchemaVersion1320(
|
||||||
|
currentVersion: number,
|
||||||
|
db: WritableDB,
|
||||||
|
logger: LoggerType
|
||||||
|
): void {
|
||||||
|
if (currentVersion >= 1320) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.transaction(() => {
|
||||||
|
const [query] = sql`
|
||||||
|
DROP INDEX unprocessed_timestamp;
|
||||||
|
|
||||||
|
ALTER TABLE unprocessed
|
||||||
|
ADD COLUMN receivedAtDate INTEGER DEFAULT 0 NOT NULL;
|
||||||
|
|
||||||
|
UPDATE unprocessed
|
||||||
|
SET receivedAtDate = timestamp;
|
||||||
|
|
||||||
|
CREATE INDEX unprocessed_byReceivedAtDate ON unprocessed
|
||||||
|
(receivedAtDate);
|
||||||
|
`;
|
||||||
|
db.exec(query);
|
||||||
|
|
||||||
|
db.pragma('user_version = 1320');
|
||||||
|
})();
|
||||||
|
|
||||||
|
logger.info('updateToSchemaVersion1320: success!');
|
||||||
|
}
|
|
@ -107,10 +107,11 @@ import { updateToSchemaVersion1270 } from './1270-normalize-messages';
|
||||||
import { updateToSchemaVersion1280 } from './1280-blob-unprocessed';
|
import { updateToSchemaVersion1280 } from './1280-blob-unprocessed';
|
||||||
import { updateToSchemaVersion1290 } from './1290-int-unprocessed-source-device';
|
import { updateToSchemaVersion1290 } from './1290-int-unprocessed-source-device';
|
||||||
import { updateToSchemaVersion1300 } from './1300-sticker-pack-refs';
|
import { updateToSchemaVersion1300 } from './1300-sticker-pack-refs';
|
||||||
|
import { updateToSchemaVersion1310 } from './1310-muted-fixup';
|
||||||
import {
|
import {
|
||||||
updateToSchemaVersion1310,
|
updateToSchemaVersion1320,
|
||||||
version as MAX_VERSION,
|
version as MAX_VERSION,
|
||||||
} from './1310-muted-fixup';
|
} from './1320-unprocessed-received-at-date';
|
||||||
import { DataWriter } from '../Server';
|
import { DataWriter } from '../Server';
|
||||||
|
|
||||||
function updateToSchemaVersion1(
|
function updateToSchemaVersion1(
|
||||||
|
@ -2089,6 +2090,7 @@ export const SCHEMA_VERSIONS = [
|
||||||
|
|
||||||
updateToSchemaVersion1300,
|
updateToSchemaVersion1300,
|
||||||
updateToSchemaVersion1310,
|
updateToSchemaVersion1310,
|
||||||
|
updateToSchemaVersion1320,
|
||||||
];
|
];
|
||||||
|
|
||||||
export class DBVersionFromFutureError extends Error {
|
export class DBVersionFromFutureError extends Error {
|
||||||
|
|
|
@ -54,6 +54,8 @@ describe('SignalProtocolStore', () => {
|
||||||
let identityKey: KeyPairType;
|
let identityKey: KeyPairType;
|
||||||
let testKey: KeyPairType;
|
let testKey: KeyPairType;
|
||||||
|
|
||||||
|
const NOW = Date.now();
|
||||||
|
|
||||||
const unprocessedDefaults = {
|
const unprocessedDefaults = {
|
||||||
type: 1,
|
type: 1,
|
||||||
messageAgeSec: 1,
|
messageAgeSec: 1,
|
||||||
|
@ -73,6 +75,7 @@ describe('SignalProtocolStore', () => {
|
||||||
|
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
content: Buffer.from('content'),
|
content: Buffer.from('content'),
|
||||||
|
timestamp: NOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getSessionRecord(isOpen?: boolean): SessionRecord {
|
function getSessionRecord(isOpen?: boolean): SessionRecord {
|
||||||
|
@ -1254,7 +1257,7 @@ describe('SignalProtocolStore', () => {
|
||||||
id: '2-two',
|
id: '2-two',
|
||||||
|
|
||||||
content: Buffer.from('second'),
|
content: Buffer.from('second'),
|
||||||
timestamp: Date.now() + 2,
|
receivedAtDate: Date.now() + 2,
|
||||||
},
|
},
|
||||||
{ zone }
|
{ zone }
|
||||||
);
|
);
|
||||||
|
@ -1312,7 +1315,7 @@ describe('SignalProtocolStore', () => {
|
||||||
id: '2-two',
|
id: '2-two',
|
||||||
|
|
||||||
content: Buffer.from('second'),
|
content: Buffer.from('second'),
|
||||||
timestamp: 2,
|
receivedAtDate: 2,
|
||||||
},
|
},
|
||||||
{ zone }
|
{ zone }
|
||||||
);
|
);
|
||||||
|
@ -1436,8 +1439,6 @@ describe('SignalProtocolStore', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Not yet processed messages', () => {
|
describe('Not yet processed messages', () => {
|
||||||
const NOW = Date.now();
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await store.removeAllUnprocessed();
|
await store.removeAllUnprocessed();
|
||||||
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
||||||
|
@ -1454,7 +1455,7 @@ describe('SignalProtocolStore', () => {
|
||||||
|
|
||||||
content: Buffer.from('old envelope'),
|
content: Buffer.from('old envelope'),
|
||||||
receivedAtCounter: -1,
|
receivedAtCounter: -1,
|
||||||
timestamp: NOW - 2 * durations.MONTH,
|
receivedAtDate: NOW - 2 * durations.MONTH,
|
||||||
}),
|
}),
|
||||||
store.addUnprocessed({
|
store.addUnprocessed({
|
||||||
...unprocessedDefaults,
|
...unprocessedDefaults,
|
||||||
|
@ -1462,7 +1463,7 @@ describe('SignalProtocolStore', () => {
|
||||||
|
|
||||||
content: Buffer.from('second'),
|
content: Buffer.from('second'),
|
||||||
receivedAtCounter: 1,
|
receivedAtCounter: 1,
|
||||||
timestamp: NOW + 2,
|
receivedAtDate: NOW + 2,
|
||||||
}),
|
}),
|
||||||
store.addUnprocessed({
|
store.addUnprocessed({
|
||||||
...unprocessedDefaults,
|
...unprocessedDefaults,
|
||||||
|
@ -1470,7 +1471,7 @@ describe('SignalProtocolStore', () => {
|
||||||
|
|
||||||
content: Buffer.from('third'),
|
content: Buffer.from('third'),
|
||||||
receivedAtCounter: 2,
|
receivedAtCounter: 2,
|
||||||
timestamp: NOW + 3,
|
receivedAtDate: NOW + 3,
|
||||||
}),
|
}),
|
||||||
store.addUnprocessed({
|
store.addUnprocessed({
|
||||||
...unprocessedDefaults,
|
...unprocessedDefaults,
|
||||||
|
@ -1478,7 +1479,7 @@ describe('SignalProtocolStore', () => {
|
||||||
|
|
||||||
content: Buffer.from('first'),
|
content: Buffer.from('first'),
|
||||||
receivedAtCounter: 0,
|
receivedAtCounter: 0,
|
||||||
timestamp: NOW + 1,
|
receivedAtDate: NOW + 1,
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -1501,7 +1502,7 @@ describe('SignalProtocolStore', () => {
|
||||||
|
|
||||||
id,
|
id,
|
||||||
|
|
||||||
timestamp: NOW + 1,
|
receivedAtDate: NOW + 1,
|
||||||
});
|
});
|
||||||
await store.removeUnprocessed(id);
|
await store.removeUnprocessed(id);
|
||||||
|
|
||||||
|
@ -1518,7 +1519,7 @@ describe('SignalProtocolStore', () => {
|
||||||
id: '1-one',
|
id: '1-one',
|
||||||
|
|
||||||
attempts: 10,
|
attempts: 10,
|
||||||
timestamp: NOW + 1,
|
receivedAtDate: NOW + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
const items = await store.getUnprocessedByIdsAndIncrementAttempts(
|
||||||
|
|
|
@ -65,6 +65,7 @@ describe('unprocessed', function (this: Mocha.Suite) {
|
||||||
sends.push(
|
sends.push(
|
||||||
alice.sendText(desktop, `hello: ${i}`, {
|
alice.sendText(desktop, `hello: ${i}`, {
|
||||||
timestamp: bootstrap.getTimestamp(),
|
timestamp: bootstrap.getTimestamp(),
|
||||||
|
sealed: i % 2 === 0,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -91,6 +92,7 @@ describe('unprocessed', function (this: Mocha.Suite) {
|
||||||
.locator(`[data-testid="${alice.device.aci}"] >> "${alice.profileName}"`)
|
.locator(`[data-testid="${alice.device.aci}"] >> "${alice.profileName}"`)
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
|
await page.locator('.module-message__text >> "hello: 4"').waitFor();
|
||||||
await page.locator('.module-message__text >> "hello: 5"').waitFor();
|
await page.locator('.module-message__text >> "hello: 5"').waitFor();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -874,9 +874,8 @@ export default class MessageReceiver
|
||||||
|
|
||||||
const envelope: ProcessedEnvelope = {
|
const envelope: ProcessedEnvelope = {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
receivedAtCounter: item.receivedAtCounter ?? item.timestamp,
|
receivedAtCounter: item.receivedAtCounter,
|
||||||
receivedAtDate:
|
receivedAtDate: item.receivedAtDate,
|
||||||
item.receivedAtCounter == null ? Date.now() : item.timestamp,
|
|
||||||
messageAgeSec: item.messageAgeSec,
|
messageAgeSec: item.messageAgeSec,
|
||||||
|
|
||||||
// Proto.Envelope fields
|
// Proto.Envelope fields
|
||||||
|
@ -1189,11 +1188,8 @@ export default class MessageReceiver
|
||||||
sourceServiceId: envelope.sourceServiceId,
|
sourceServiceId: envelope.sourceServiceId,
|
||||||
sourceDevice: envelope.sourceDevice,
|
sourceDevice: envelope.sourceDevice,
|
||||||
destinationServiceId: envelope.destinationServiceId,
|
destinationServiceId: envelope.destinationServiceId,
|
||||||
|
timestamp: envelope.timestamp,
|
||||||
// This field is only used for aging items out of the cache. The original
|
receivedAtDate: envelope.receivedAtDate,
|
||||||
// envelope's timestamp will be used when retrying this item.
|
|
||||||
timestamp: envelope.receivedAtDate,
|
|
||||||
|
|
||||||
attempts: 0,
|
attempts: 0,
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
content: envelope.content,
|
content: envelope.content,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue