Fix timestamp capping for storage service
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
parent
5fc53ee435
commit
f15d5049f8
5 changed files with 141 additions and 7 deletions
|
@ -888,7 +888,10 @@ export async function mergeGroupV1Record(
|
||||||
});
|
});
|
||||||
|
|
||||||
conversation.setMuteExpiration(
|
conversation.setMuteExpiration(
|
||||||
getTimestampFromLong(groupV1Record.mutedUntilTimestamp),
|
getTimestampFromLong(
|
||||||
|
groupV1Record.mutedUntilTimestamp,
|
||||||
|
Number.MAX_SAFE_INTEGER
|
||||||
|
),
|
||||||
{
|
{
|
||||||
viaStorageServiceSync: true,
|
viaStorageServiceSync: true,
|
||||||
}
|
}
|
||||||
|
@ -1025,7 +1028,10 @@ export async function mergeGroupV2Record(
|
||||||
});
|
});
|
||||||
|
|
||||||
conversation.setMuteExpiration(
|
conversation.setMuteExpiration(
|
||||||
getTimestampFromLong(groupV2Record.mutedUntilTimestamp),
|
getTimestampFromLong(
|
||||||
|
groupV2Record.mutedUntilTimestamp,
|
||||||
|
Number.MAX_SAFE_INTEGER
|
||||||
|
),
|
||||||
{
|
{
|
||||||
viaStorageServiceSync: true,
|
viaStorageServiceSync: true,
|
||||||
}
|
}
|
||||||
|
@ -1265,7 +1271,10 @@ export async function mergeContactRecord(
|
||||||
}
|
}
|
||||||
|
|
||||||
conversation.setMuteExpiration(
|
conversation.setMuteExpiration(
|
||||||
getTimestampFromLong(contactRecord.mutedUntilTimestamp),
|
getTimestampFromLong(
|
||||||
|
contactRecord.mutedUntilTimestamp,
|
||||||
|
Number.MAX_SAFE_INTEGER
|
||||||
|
),
|
||||||
{
|
{
|
||||||
viaStorageServiceSync: true,
|
viaStorageServiceSync: true,
|
||||||
}
|
}
|
||||||
|
|
40
ts/sql/migrations/1310-muted-fixup.ts
Normal file
40
ts/sql/migrations/1310-muted-fixup.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// 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 = 1310;
|
||||||
|
|
||||||
|
// Value from ts/util/timestamp.ts at the time of creation of this migration
|
||||||
|
const MAX_SAFE_DATE = 8640000000000000;
|
||||||
|
|
||||||
|
export function updateToSchemaVersion1310(
|
||||||
|
currentVersion: number,
|
||||||
|
db: WritableDB,
|
||||||
|
logger: LoggerType
|
||||||
|
): void {
|
||||||
|
if (currentVersion >= 1310) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.transaction(() => {
|
||||||
|
const [query, params] = sql`
|
||||||
|
UPDATE conversations
|
||||||
|
SET json = json_replace(
|
||||||
|
json,
|
||||||
|
'$.muteExpiresAt',
|
||||||
|
9007199254740991 -- max safe integer
|
||||||
|
)
|
||||||
|
WHERE json ->> '$.muteExpiresAt' IS ${MAX_SAFE_DATE};
|
||||||
|
`;
|
||||||
|
const { changes } = db.prepare(query).run(params);
|
||||||
|
if (changes !== 0) {
|
||||||
|
logger.warn(`updateToSchemaVersion1310: fixed ${changes} conversations`);
|
||||||
|
}
|
||||||
|
|
||||||
|
db.pragma('user_version = 1310');
|
||||||
|
})();
|
||||||
|
|
||||||
|
logger.info('updateToSchemaVersion1310: success!');
|
||||||
|
}
|
|
@ -106,10 +106,11 @@ import { updateToSchemaVersion1260 } from './1260-sync-tasks-rowid';
|
||||||
import { updateToSchemaVersion1270 } from './1270-normalize-messages';
|
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 {
|
import {
|
||||||
updateToSchemaVersion1300,
|
updateToSchemaVersion1310,
|
||||||
version as MAX_VERSION,
|
version as MAX_VERSION,
|
||||||
} from './1300-sticker-pack-refs';
|
} from './1310-muted-fixup';
|
||||||
import { DataWriter } from '../Server';
|
import { DataWriter } from '../Server';
|
||||||
|
|
||||||
function updateToSchemaVersion1(
|
function updateToSchemaVersion1(
|
||||||
|
@ -2087,6 +2088,7 @@ export const SCHEMA_VERSIONS = [
|
||||||
updateToSchemaVersion1290,
|
updateToSchemaVersion1290,
|
||||||
|
|
||||||
updateToSchemaVersion1300,
|
updateToSchemaVersion1300,
|
||||||
|
updateToSchemaVersion1310,
|
||||||
];
|
];
|
||||||
|
|
||||||
export class DBVersionFromFutureError extends Error {
|
export class DBVersionFromFutureError extends Error {
|
||||||
|
|
80
ts/test-node/sql/migration_1310_test.ts
Normal file
80
ts/test-node/sql/migration_1310_test.ts
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { assert } from 'chai';
|
||||||
|
|
||||||
|
import { type WritableDB } from '../../sql/Interface';
|
||||||
|
import { createDB, updateToVersion, insertData, getTableData } from './helpers';
|
||||||
|
|
||||||
|
describe('SQL/updateToSchemaVersion1310', () => {
|
||||||
|
let db: WritableDB;
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
db = createDB();
|
||||||
|
updateToVersion(db, 1300);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves absent muteExpiresAt untouched', () => {
|
||||||
|
const convos = [
|
||||||
|
{
|
||||||
|
id: 'convo',
|
||||||
|
expireTimerVersion: 1,
|
||||||
|
json: {},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
insertData(db, 'conversations', convos);
|
||||||
|
updateToVersion(db, 1310);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(getTableData(db, 'conversations'), convos);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('leaves regular muteExpiresAt untouched', () => {
|
||||||
|
const convos = [
|
||||||
|
{
|
||||||
|
id: 'convo',
|
||||||
|
expireTimerVersion: 1,
|
||||||
|
json: {
|
||||||
|
muteExpiresAt: 123,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'convo-2',
|
||||||
|
expireTimerVersion: 1,
|
||||||
|
json: {
|
||||||
|
muteExpiresAt: 8640000000000000 - 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
insertData(db, 'conversations', convos);
|
||||||
|
updateToVersion(db, 1310);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(getTableData(db, 'conversations'), convos);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('promotes MAX_SAFE_DATE to MAX_SAFE_INTEGER', () => {
|
||||||
|
insertData(db, 'conversations', [
|
||||||
|
{
|
||||||
|
id: 'convo',
|
||||||
|
expireTimerVersion: 1,
|
||||||
|
json: {
|
||||||
|
muteExpiresAt: 8640000000000000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
updateToVersion(db, 1310);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(getTableData(db, 'conversations'), [
|
||||||
|
{
|
||||||
|
id: 'convo',
|
||||||
|
expireTimerVersion: 1,
|
||||||
|
json: {
|
||||||
|
muteExpiresAt: Number.MAX_SAFE_INTEGER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -19,7 +19,10 @@ export function getSafeLongFromTimestamp(
|
||||||
return Long.fromNumber(timestamp);
|
return Long.fromNumber(timestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTimestampFromLong(value?: Long | null): number {
|
export function getTimestampFromLong(
|
||||||
|
value?: Long | null,
|
||||||
|
maxValue = MAX_SAFE_DATE
|
||||||
|
): number {
|
||||||
if (!value || value.isNegative()) {
|
if (!value || value.isNegative()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -27,7 +30,7 @@ export function getTimestampFromLong(value?: Long | null): number {
|
||||||
const num = value.toNumber();
|
const num = value.toNumber();
|
||||||
|
|
||||||
if (num > MAX_SAFE_DATE) {
|
if (num > MAX_SAFE_DATE) {
|
||||||
return MAX_SAFE_DATE;
|
return maxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return num;
|
return num;
|
||||||
|
|
Loading…
Add table
Reference in a new issue