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(
 | 
			
		||||
    getTimestampFromLong(groupV1Record.mutedUntilTimestamp),
 | 
			
		||||
    getTimestampFromLong(
 | 
			
		||||
      groupV1Record.mutedUntilTimestamp,
 | 
			
		||||
      Number.MAX_SAFE_INTEGER
 | 
			
		||||
    ),
 | 
			
		||||
    {
 | 
			
		||||
      viaStorageServiceSync: true,
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1025,7 +1028,10 @@ export async function mergeGroupV2Record(
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  conversation.setMuteExpiration(
 | 
			
		||||
    getTimestampFromLong(groupV2Record.mutedUntilTimestamp),
 | 
			
		||||
    getTimestampFromLong(
 | 
			
		||||
      groupV2Record.mutedUntilTimestamp,
 | 
			
		||||
      Number.MAX_SAFE_INTEGER
 | 
			
		||||
    ),
 | 
			
		||||
    {
 | 
			
		||||
      viaStorageServiceSync: true,
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1265,7 +1271,10 @@ export async function mergeContactRecord(
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  conversation.setMuteExpiration(
 | 
			
		||||
    getTimestampFromLong(contactRecord.mutedUntilTimestamp),
 | 
			
		||||
    getTimestampFromLong(
 | 
			
		||||
      contactRecord.mutedUntilTimestamp,
 | 
			
		||||
      Number.MAX_SAFE_INTEGER
 | 
			
		||||
    ),
 | 
			
		||||
    {
 | 
			
		||||
      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 { updateToSchemaVersion1280 } from './1280-blob-unprocessed';
 | 
			
		||||
import { updateToSchemaVersion1290 } from './1290-int-unprocessed-source-device';
 | 
			
		||||
import { updateToSchemaVersion1300 } from './1300-sticker-pack-refs';
 | 
			
		||||
import {
 | 
			
		||||
  updateToSchemaVersion1300,
 | 
			
		||||
  updateToSchemaVersion1310,
 | 
			
		||||
  version as MAX_VERSION,
 | 
			
		||||
} from './1300-sticker-pack-refs';
 | 
			
		||||
} from './1310-muted-fixup';
 | 
			
		||||
import { DataWriter } from '../Server';
 | 
			
		||||
 | 
			
		||||
function updateToSchemaVersion1(
 | 
			
		||||
| 
						 | 
				
			
			@ -2087,6 +2088,7 @@ export const SCHEMA_VERSIONS = [
 | 
			
		|||
  updateToSchemaVersion1290,
 | 
			
		||||
 | 
			
		||||
  updateToSchemaVersion1300,
 | 
			
		||||
  updateToSchemaVersion1310,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getTimestampFromLong(value?: Long | null): number {
 | 
			
		||||
export function getTimestampFromLong(
 | 
			
		||||
  value?: Long | null,
 | 
			
		||||
  maxValue = MAX_SAFE_DATE
 | 
			
		||||
): number {
 | 
			
		||||
  if (!value || value.isNegative()) {
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +30,7 @@ export function getTimestampFromLong(value?: Long | null): number {
 | 
			
		|||
  const num = value.toNumber();
 | 
			
		||||
 | 
			
		||||
  if (num > MAX_SAFE_DATE) {
 | 
			
		||||
    return MAX_SAFE_DATE;
 | 
			
		||||
    return maxValue;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return num;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue