Support for global.messageQueueTimeInSeconds

Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
automated-signal 2024-10-15 18:12:03 -05:00 committed by GitHub
parent 23e3a847d1
commit 6cc07a4d17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 57 additions and 31 deletions

View file

@ -36,6 +36,7 @@ export type ConfigKeyType =
| 'global.calling.maxGroupCallRingSize'
| 'global.groupsv2.groupSizeHardLimit'
| 'global.groupsv2.maxGroupSize'
| 'global.messageQueueTimeInSeconds'
| 'global.nicknames.max'
| 'global.nicknames.min'
| 'global.textAttachmentLimitBytes';

View file

@ -44,6 +44,7 @@ import { storageJobQueue } from '../util/JobQueue';
import { sleep } from '../util/sleep';
import { isMoreRecentThan, isOlderThan } from '../util/timestamp';
import { map, filter } from '../util/iterables';
import { getMessageQueueTime } from '../util/getMessageQueueTime';
import { ourProfileKeyService } from './ourProfileKey';
import {
ConversationTypes,
@ -350,7 +351,10 @@ async function generateManifest(
if (
storyDistributionList.deletedAtTimestamp != null &&
isOlderThan(storyDistributionList.deletedAtTimestamp, durations.MONTH)
isOlderThan(
storyDistributionList.deletedAtTimestamp,
getMessageQueueTime()
)
) {
const droppedID = storyDistributionList.storageID;
const droppedVersion = storyDistributionList.storageVersion;
@ -1316,7 +1320,7 @@ async function processManifest(
'unregistered and not in remote manifest'
);
conversation.setUnregistered({
timestamp: Date.now() - durations.MONTH,
timestamp: Date.now() - getMessageQueueTime(),
fromStorageService: true,
// Saving below

View file

@ -72,11 +72,9 @@ import {
fromRootKeyBytes,
getRoomIdFromRootKey,
} from '../util/callLinksRingrtc';
import {
CALL_LINK_DELETED_STORAGE_RECORD_TTL,
fromAdminKeyBytes,
} from '../util/callLinks';
import { fromAdminKeyBytes } from '../util/callLinks';
import { isOlderThan } from '../util/timestamp';
import { getMessageQueueTime } from '../util/getMessageQueueTime';
import { callLinkRefreshJobQueue } from '../jobs/callLinkRefreshJobQueue';
const MY_STORY_BYTES = uuidToBytes(MY_STORY_ID);
@ -1981,8 +1979,7 @@ export async function mergeCallLinkRecord(
? getTimestampFromLong(callLinkRecord.deletedAtTimestampMs)
: null;
const shouldDrop =
deletedAt != null &&
isOlderThan(deletedAt, CALL_LINK_DELETED_STORAGE_RECORD_TTL);
deletedAt != null && isOlderThan(deletedAt, getMessageQueueTime());
if (shouldDrop) {
details.push('expired deleted call link; scheduling for removal');
}

View file

@ -4,7 +4,7 @@
import { debounce } from 'lodash';
import { DataReader } from '../sql/Client';
import { clearTimeoutIfNecessary } from '../util/clearTimeoutIfNecessary';
import { DAY } from '../util/durations';
import { getMessageQueueTime } from '../util/getMessageQueueTime';
import * as Errors from '../types/errors';
async function eraseTapToViewMessages() {
@ -12,7 +12,9 @@ async function eraseTapToViewMessages() {
window.SignalContext.log.info(
'eraseTapToViewMessages: Loading messages...'
);
const messages = await DataReader.getTapToViewMessagesNeedingErase();
const maxTimestamp = Date.now() - getMessageQueueTime();
const messages =
await DataReader.getTapToViewMessagesNeedingErase(maxTimestamp);
await Promise.all(
messages.map(async fromDB => {
const message = window.MessageCache.__DEPRECATED$register(
@ -59,7 +61,7 @@ class TapToViewMessagesDeletionService {
return;
}
const nextCheck = receivedAt + 30 * DAY;
const nextCheck = receivedAt + getMessageQueueTime();
window.SignalContext.log.info(
'checkTapToViewMessages: next check at',
new Date(nextCheck).toISOString()

View file

@ -541,7 +541,9 @@ type ReadableInterface = {
getMessagesUnexpectedlyMissingExpirationStartTimestamp: () => Array<MessageType>;
getSoonestMessageExpiry: () => undefined | number;
getNextTapToViewMessageTimestampToAgeOut: () => undefined | number;
getTapToViewMessagesNeedingErase: () => Array<MessageType>;
getTapToViewMessagesNeedingErase: (
maxTimestamp: number
) => Array<MessageType>;
// getOlderMessagesByConversation is JSON on server, full message on Client
getAllStories: (options: {
conversationId?: string;

View file

@ -4524,9 +4524,10 @@ function getNextTapToViewMessageTimestampToAgeOut(
return isNormalNumber(result) ? result : undefined;
}
function getTapToViewMessagesNeedingErase(db: ReadableDB): Array<MessageType> {
const THIRTY_DAYS_AGO = Date.now() - 30 * 24 * 60 * 60 * 1000;
function getTapToViewMessagesNeedingErase(
db: ReadableDB,
maxTimestamp: number
): Array<MessageType> {
const rows: JSONRows = db
.prepare<Query>(
`
@ -4535,12 +4536,12 @@ function getTapToViewMessagesNeedingErase(db: ReadableDB): Array<MessageType> {
WHERE
isViewOnce = 1
AND (isErased IS NULL OR isErased != 1)
AND received_at <= $THIRTY_DAYS_AGO
AND received_at <= $maxTimestamp
ORDER BY received_at ASC, sent_at ASC;
`
)
.all({
THIRTY_DAYS_AGO,
maxTimestamp,
});
return rows.map(row => jsonToObject(row.json));

View file

@ -59,6 +59,7 @@ import * as log from '../logging/log';
import type { StorageAccessType } from '../types/Storage';
import { getRelativePath, createName } from '../util/attachmentPath';
import { isBackupEnabled } from '../util/isBackupEnabled';
import { getMessageQueueTime } from '../util/getMessageQueueTime';
type StorageKeyByServiceIdKind = {
[kind in ServiceIdKind]: keyof StorageAccessType;
@ -77,7 +78,6 @@ export const KYBER_KEY_ID_KEY: StorageKeyByServiceIdKind = {
[ServiceIdKind.PNI]: 'maxKyberPreKeyIdPNI',
};
const LAST_RESORT_KEY_ARCHIVE_AGE = 30 * DAY;
const LAST_RESORT_KEY_ROTATION_AGE = DAY * 1.5;
const LAST_RESORT_KEY_MINIMUM = 5;
const LAST_RESORT_KEY_UPDATE_TIME_KEY: StorageKeyByServiceIdKind = {
@ -96,7 +96,6 @@ const PRE_KEY_ID_KEY: StorageKeyByServiceIdKind = {
};
const PRE_KEY_MINIMUM = 10;
const SIGNED_PRE_KEY_ARCHIVE_AGE = 30 * DAY;
export const SIGNED_PRE_KEY_ID_KEY: StorageKeyByServiceIdKind = {
[ServiceIdKind.ACI]: 'signedKeyId',
[ServiceIdKind.Unknown]: 'signedKeyId',
@ -756,7 +755,7 @@ export default class AccountManager extends EventTarget {
'confirmed'
);
// Keep SIGNED_PRE_KEY_MINIMUM keys, drop if older than SIGNED_PRE_KEY_ARCHIVE_AGE
// Keep SIGNED_PRE_KEY_MINIMUM keys, drop if older than message queue time
const toDelete: Array<number> = [];
sortedKeys.forEach((key, index) => {
@ -765,7 +764,7 @@ export default class AccountManager extends EventTarget {
}
const createdAt = key.created_at || 0;
if (isOlderThan(createdAt, SIGNED_PRE_KEY_ARCHIVE_AGE)) {
if (isOlderThan(createdAt, getMessageQueueTime())) {
const timestamp = new Date(createdAt).toJSON();
const confirmedText = key.confirmed ? ' (confirmed)' : '';
log.info(
@ -813,7 +812,7 @@ export default class AccountManager extends EventTarget {
'confirmed'
);
// Keep LAST_RESORT_KEY_MINIMUM keys, drop if older than LAST_RESORT_KEY_ARCHIVE_AGE
// Keep LAST_RESORT_KEY_MINIMUM keys, drop if older than message queue time
const toDelete: Array<number> = [];
sortedKeys.forEach((key, index) => {
@ -822,7 +821,7 @@ export default class AccountManager extends EventTarget {
}
const createdAt = key.createdAt || 0;
if (isOlderThan(createdAt, LAST_RESORT_KEY_ARCHIVE_AGE)) {
if (isOlderThan(createdAt, getMessageQueueTime())) {
const timestamp = new Date(createdAt).toJSON();
const confirmedText = key.isConfirmed ? ' (confirmed)' : '';
log.info(

View file

@ -30,6 +30,7 @@ import { strictAssert } from '../util/assert';
import type { SignalService as Proto } from '../protobuf';
import { isMoreRecentThan } from '../util/timestamp';
import { DAY } from '../util/durations';
import { getMessageQueueTime } from '../util/getMessageQueueTime';
import { getLocalAttachmentUrl } from '../util/getLocalAttachmentUrl';
import type { ReencryptionInfo } from '../AttachmentCrypto';
@ -1156,10 +1157,9 @@ export function isReencryptableWithNewEncryptionInfo(
);
}
const TIME_ON_TRANSIT_TIER = 30 * DAY;
// Extend range in case the attachment is actually still there (this function is meant to
// be optimistic)
const BUFFERED_TIME_ON_TRANSIT_TIER = TIME_ON_TRANSIT_TIER + 5 * DAY;
const BUFFER_TIME_ON_TRANSIT_TIER = 5 * DAY;
export function mightStillBeOnTransitTier(
attachment: Pick<AttachmentType, 'cdnKey' | 'cdnNumber' | 'uploadTimestamp'>
@ -1177,7 +1177,10 @@ export function mightStillBeOnTransitTier(
}
if (
isMoreRecentThan(attachment.uploadTimestamp, BUFFERED_TIME_ON_TRANSIT_TIER)
isMoreRecentThan(
attachment.uploadTimestamp,
getMessageQueueTime() + BUFFER_TIME_ON_TRANSIT_TIER
)
) {
return true;
}

View file

@ -15,7 +15,6 @@ import {
type CallHistoryDetails,
CallMode,
} from '../types/CallDisposition';
import { DAY } from './durations';
export const CALL_LINK_DEFAULT_STATE: Pick<
CallLinkType,
@ -28,8 +27,6 @@ export const CALL_LINK_DEFAULT_STATE: Pick<
storageNeedsSync: false,
};
export const CALL_LINK_DELETED_STORAGE_RECORD_TTL = 30 * DAY;
export function getKeyFromCallLink(callLink: string): string {
const url = new URL(callLink);
if (url == null) {

View file

@ -0,0 +1,18 @@
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as RemoteConfig from '../RemoteConfig';
import { MONTH, SECOND } from './durations';
import { parseIntWithFallback } from './parseIntWithFallback';
export function getMessageQueueTime(): number {
return (
Math.max(
parseIntWithFallback(
RemoteConfig.getValue('global.messageQueueTimeInSeconds'),
MONTH / SECOND
),
MONTH / SECOND
) * SECOND
);
}

View file

@ -3,7 +3,8 @@
import type { ServiceIdString } from '../types/ServiceId';
import { isMoreRecentThan, isOlderThan } from './timestamp';
import { HOUR, MONTH } from './durations';
import { HOUR } from './durations';
import { getMessageQueueTime } from './getMessageQueueTime';
const SIX_HOURS = 6 * HOUR;
@ -45,6 +46,7 @@ export function isConversationUnregisteredAndStale({
}
return Boolean(
firstUnregisteredAt && isOlderThan(firstUnregisteredAt, MONTH)
firstUnregisteredAt &&
isOlderThan(firstUnregisteredAt, getMessageQueueTime())
);
}