2018-04-07 22:59:47 -04:00
"use strict" ;
2024-07-18 21:35:17 +03:00
import sql from "./sql.js" ;
2025-01-02 13:47:44 +01:00
import { hash } from "./utils.js" ;
2024-07-18 21:35:17 +03:00
import log from "./log.js" ;
import eraseService from "./erase.js" ;
2024-02-18 12:50:48 +02:00
type SectorHash = Record < string , string > ;
2018-05-22 00:15:54 -04:00
2020-06-20 12:31:38 +02:00
function getEntityHashes() {
2023-07-27 23:57:12 +02:00
// blob erasure is not synced, we should check before each sync if there's some blob to erase
eraseService . eraseUnusedBlobs ( ) ;
2018-01-01 19:47:50 -05:00
const startTime = new Date ( ) ;
2023-10-20 09:36:57 +02:00
// we know this is slow and the total content hash calculation time is logged
2025-01-09 18:07:02 +02:00
type HashRow = [ string , string , string , boolean ] ;
const hashRows = sql . disableSlowQueryLogging ( ( ) = >
sql . getRawRows < HashRow > ( `
2023-10-20 09:36:57 +02:00
SELECT entityName ,
2024-12-22 15:42:15 +02:00
entityId ,
hash ,
isErased
2023-10-20 09:36:57 +02:00
FROM entity_changes
WHERE isSynced = 1
2024-12-22 15:42:15 +02:00
AND entityName != 'note_reordering' ` )
2023-10-20 09:36:57 +02:00
) ;
2020-12-07 23:04:17 +01:00
// sorting is faster in memory
// sorting by entityId is enough, hashes will be segmented by entityName later on anyway
2025-01-09 18:07:02 +02:00
hashRows . sort ( ( a , b ) = > ( a [ 1 ] < b [ 1 ] ? - 1 : 1 ) ) ;
2020-12-07 23:04:17 +01:00
2024-02-18 12:50:48 +02:00
const hashMap : Record < string , SectorHash > = { } ;
2020-12-07 23:04:17 +01:00
2023-07-29 21:59:20 +02:00
for ( const [ entityName , entityId , hash , isErased ] of hashRows ) {
2025-01-09 18:07:02 +02:00
const entityHashMap = ( hashMap [ entityName ] = hashMap [ entityName ] || { } ) ;
2020-12-07 23:04:17 +01:00
const sector = entityId [ 0 ] ;
2023-07-29 21:59:20 +02:00
// if the entity is erased, its hash is not updated, so it has to be added extra
entityHashMap [ sector ] = ( entityHashMap [ sector ] || "" ) + hash + isErased ;
2020-12-07 23:04:17 +01:00
}
for ( const entityHashMap of Object . values ( hashMap ) ) {
for ( const key in entityHashMap ) {
2025-01-02 13:47:44 +01:00
entityHashMap [ key ] = hash ( entityHashMap [ key ] ) ;
2020-12-07 23:04:17 +01:00
}
}
2018-01-01 19:47:50 -05:00
2019-12-18 22:24:13 +01:00
const elapsedTimeMs = Date . now ( ) - startTime . getTime ( ) ;
2018-01-01 19:47:50 -05:00
2019-12-18 22:24:13 +01:00
log . info ( ` Content hash computation took ${ elapsedTimeMs } ms ` ) ;
2018-01-01 19:47:50 -05:00
2020-12-07 23:04:17 +01:00
return hashMap ;
2017-11-21 22:11:27 -05:00
}
2024-02-18 12:50:48 +02:00
function checkContentHashes ( otherHashes : Record < string , SectorHash > ) {
2020-06-20 12:31:38 +02:00
const entityHashes = getEntityHashes ( ) ;
2019-12-18 22:24:13 +01:00
const failedChecks = [ ] ;
2018-04-07 22:59:47 -04:00
2019-12-18 22:24:13 +01:00
for ( const entityName in entityHashes ) {
2024-02-18 12:50:48 +02:00
const thisSectorHashes : SectorHash = entityHashes [ entityName ] || { } ;
const otherSectorHashes : SectorHash = otherHashes [ entityName ] || { } ;
2018-04-07 22:59:47 -04:00
2019-12-24 16:00:31 +01:00
const sectors = new Set ( Object . keys ( thisSectorHashes ) . concat ( Object . keys ( otherSectorHashes ) ) ) ;
2019-11-19 19:07:14 +01:00
2019-12-18 22:24:13 +01:00
for ( const sector of sectors ) {
if ( thisSectorHashes [ sector ] !== otherSectorHashes [ sector ] ) {
log . info ( ` Content hash check for ${ entityName } sector ${ sector } FAILED. Local is ${ thisSectorHashes [ sector ] } , remote is ${ otherSectorHashes [ sector ] } ` ) ;
failedChecks . push ( { entityName , sector } ) ;
2018-04-07 22:59:47 -04:00
}
}
}
2019-12-18 22:24:13 +01:00
if ( failedChecks . length === 0 ) {
2018-04-07 22:59:47 -04:00
log . info ( "Content hash checks PASSED" ) ;
}
2019-12-18 22:58:30 +01:00
return failedChecks ;
2018-04-07 22:59:47 -04:00
}
2024-07-18 21:47:30 +03:00
export default {
2019-12-18 22:58:30 +01:00
getEntityHashes ,
2018-04-07 22:59:47 -04:00
checkContentHashes
2020-06-20 12:31:38 +02:00
} ;