tests, fix: execute in parallel, send connected events after creating chat items, fix race when merging multiple members to one contact (#7500)

* tests: execute in parallel

* remove race in terminal

* fix some tests

* stop chat controller on exit

* fix more tests

* fix tests

* query plans

* update simplexmq

* update direct-sqlcipher

* fix tests

* query plans

* ios: update core

* fix tests

* update simplexmq and direct-sqlcipher, fixes

* simplexmq

* update simplexmq

* parallel channel tests

* update tests

* fix tests

* fix tests

* query plans

* fix test

* more tests in CI

* use ram drive in CI

* fix tests, fix directory deleting voice file before it is uploaded

* fix query

* fix query to use index

* link contact and channel owner profile when connecting via a signed link

* update simplexmq, fix ghc 8.10.7

* fix tests

* avoid double stop

* query plans

* fix more test races

* more test fixes

* fix queries

* fixes

* more fixes

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
Evgeny
2026-10-07 08:12:02 +01:00
committed by GitHub
co-authored by Evgeny @ SimpleX Chat
parent 680790695e
commit df95de8a2c
35 changed files with 1773 additions and 1167 deletions
+1 -1
View File
@@ -1169,6 +1169,6 @@ getDirectChatTTL db ctId =
getUserContactsToExpire :: DB.Connection -> User -> Int64 -> IO [ContactId]
getUserContactsToExpire db User {userId} globalTTL =
map fromOnly <$> DB.query db ("SELECT contact_id FROM contacts WHERE user_id = ? AND chat_item_ttl > 0" <> cond) (Only userId)
map fromOnly <$> DB.query db ("SELECT contact_id FROM contacts WHERE user_id = ? AND (chat_item_ttl > 0" <> cond <> ")") (Only userId)
where
cond = if globalTTL == 0 then "" else " OR chat_item_ttl IS NULL"
+21 -7
View File
@@ -3105,10 +3105,10 @@ associateContactWithMemberRecord
db
[sql|
UPDATE group_members
SET contact_id = ?, updated_at = ?
WHERE user_id = ? AND group_id = ? AND group_member_id = ?
SET contact_id = ?, local_display_name = ?, contact_profile_id = ?, updated_at = ?
WHERE (user_id = ? AND group_id = ? AND group_member_id = ?) OR contact_id = ?
|]
(contactId, currentTs, userId, groupId, groupMemberId)
(contactId, memLDN, memProfileId, currentTs, userId, groupId, groupMemberId, contactId)
DB.execute
db
[sql|
@@ -3412,7 +3412,16 @@ setMemberContactStartedConnection db Contact {contactId} = do
(BI True, currentTs, contactId)
updateMemberProfile :: DB.Connection -> StoreCxt -> User -> GroupMember -> Profile -> ExceptT StoreError IO GroupMember
updateMemberProfile db cxt user@User {userId} m p' = do
updateMemberProfile db cxt user m@GroupMember {memberContactId} p' = case memberContactId of
Nothing -> updateUnlinkedMemberProfile db cxt user m p'
Just ctId -> do
ct <- getContact db cxt user ctId
if contactUpdatableFromMember ct
then fst <$> updateContactMemberProfile db cxt user m ct p'
else pure m
updateUnlinkedMemberProfile :: DB.Connection -> StoreCxt -> User -> GroupMember -> Profile -> ExceptT StoreError IO GroupMember
updateUnlinkedMemberProfile db cxt user@User {userId} m p' = do
currentTs <- liftIO getCurrentTime
badgeVerified <- liftIO $ profileBadgeVerified (badgeKeys cxt) (memberProfile m) p'
let memberProfile = toLocalProfile profileId p' localAlias currentTs badgeVerified Nothing
@@ -3494,8 +3503,7 @@ createNewUnknownGroupMember db cxt user@User {userId, userContactId} GroupInfo {
createLinkOwnerMember :: DB.Connection -> StoreCxt -> User -> GroupInfo -> Maybe ContactId -> MemberId -> C.PublicKeyEd25519 -> ExceptT StoreError IO GroupMember
createLinkOwnerMember db cxt user@User {userId, userContactId} GroupInfo {groupId} contactId_ memberId ownerKey = do
currentTs <- liftIO getCurrentTime
let memberProfile = profileFromName $ nameFromMemberId memberId
(localDisplayName, profileId, _) <- createNewMemberProfile_ db cxt user memberProfile currentTs
(localDisplayName, profileId) <- maybe (newOwnerProfile currentTs) contactNameAndProfile contactId_
indexInGroup <- getUpdateNextIndexInGroup_ db groupId
liftIO $
DB.execute
@@ -3515,6 +3523,12 @@ createLinkOwnerMember db cxt user@User {userId, userContactId} GroupInfo {groupI
getGroupMemberById db cxt user groupMemberId
where
VersionRange minV maxV = vr cxt
newOwnerProfile currentTs = do
(ldn, pId, _) <- createNewMemberProfile_ db cxt user (profileFromName $ nameFromMemberId memberId) currentTs
pure (ldn, pId)
contactNameAndProfile ctId = do
Contact {localDisplayName = ldn, profile = LocalProfile {profileId = pId}} <- getContact db cxt user ctId
pure (ldn, pId)
-- Intro refreshes only profile / status / peer version. Role and key stay owner-authoritative
-- (the owner-signed roster for members/moderators/admins, link data for owners), so taking either from
@@ -3650,7 +3664,7 @@ getGroupChatTTL db gId =
getUserGroupsToExpire :: DB.Connection -> User -> Int64 -> IO [GroupId]
getUserGroupsToExpire db User {userId} globalTTL =
map fromOnly <$> DB.query db ("SELECT group_id FROM groups WHERE user_id = ? AND chat_item_ttl > 0" <> cond) (Only userId)
map fromOnly <$> DB.query db ("SELECT group_id FROM groups WHERE user_id = ? AND (chat_item_ttl > 0" <> cond <> ")") (Only userId)
where
cond = if globalTTL == 0 then "" else " OR chat_item_ttl IS NULL"
@@ -293,6 +293,16 @@ Query:
Plan:
SEARCH connections USING PRIMARY KEY (conn_id=?)
Query:
SELECT user_id FROM users u
WHERE u.deleted = ?
AND NOT EXISTS (SELECT c.conn_id FROM connections c WHERE c.user_id = u.user_id)
Plan:
SCAN u
CORRELATED SCALAR SUBQUERY 1
SEARCH c USING COVERING INDEX idx_connections_user (user_id=?)
Query:
SELECT user_id FROM users u
WHERE u.user_id = ?
@@ -515,6 +525,23 @@ Query:
Plan:
SEARCH conn_confirmations USING INDEX idx_conn_confirmations_conn_id (conn_id=?)
Query:
SELECT conn_id, (
SELECT processed_ratchet_key_hash_id
FROM processed_ratchet_key_hashes
WHERE conn_id = c.conn_id
ORDER BY processed_ratchet_key_hash_id DESC
LIMIT 1 OFFSET ?
) AS max_excess_id
FROM processed_ratchet_key_hashes c
GROUP BY conn_id
HAVING COUNT(*) > ?
Plan:
SCAN c USING COVERING INDEX idx_processed_ratchet_key_hashes_conn_id
CORRELATED SCALAR SUBQUERY 1
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_conn_id (conn_id=?)
Query:
SELECT conn_id, ratchet_state, sender_key, e2e_snd_pub_key, sender_conn_info, smp_reply_queues, smp_client_version
FROM conn_confirmations
@@ -587,6 +614,15 @@ Query:
Plan:
SEARCH address_ratchet_keys USING INDEX idx_address_ratchet_keys (conn_id=? AND ratchet_key_id=?)
Query:
UPDATE rcv_messages
SET receive_attempts = receive_attempts + 1
WHERE conn_id = ? AND internal_id = ?
RETURNING receive_attempts
Plan:
SEARCH rcv_messages USING COVERING INDEX idx_rcv_messages_conn_id_internal_id (conn_id=? AND internal_id=?)
Query:
DELETE FROM address_ratchet_keys
WHERE conn_id = ?
@@ -611,6 +647,39 @@ Query:
Plan:
SEARCH conn_confirmations USING COVERING INDEX idx_conn_confirmations_conn_id (conn_id=?)
Query:
DELETE FROM encrypted_rcv_message_hashes
WHERE encrypted_rcv_message_hash_id IN (
SELECT encrypted_rcv_message_hash_id
FROM encrypted_rcv_message_hashes
WHERE created_at < ?
ORDER BY created_at ASC
LIMIT ?
)
Plan:
SEARCH encrypted_rcv_message_hashes USING INTEGER PRIMARY KEY (rowid=?)
LIST SUBQUERY 1
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_created_at (created_at<?)
Query:
DELETE FROM messages
WHERE (conn_id, internal_id) IN (
SELECT conn_id, internal_id
FROM messages
WHERE internal_ts < ? AND internal_snd_id IS NOT NULL
ORDER BY internal_ts ASC
LIMIT ?
)
Plan:
SEARCH messages USING COVERING INDEX idx_messages_conn_id (conn_id=? AND internal_id=?)
LIST SUBQUERY 1
SEARCH messages USING INDEX idx_messages_internal_ts (internal_ts<?)
SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_conn_id_internal_id (conn_id=? AND internal_id=?)
SEARCH snd_messages USING COVERING INDEX idx_snd_messages_conn_id_internal_id (conn_id=? AND internal_id=?)
SEARCH rcv_messages USING COVERING INDEX idx_rcv_messages_conn_id_internal_id (conn_id=? AND internal_id=?)
Query:
INSERT INTO address_ratchet_keys
(conn_id, ratchet_key_id, x3dh_priv_key_1, x3dh_priv_key_2, pq_priv_kem)
@@ -756,6 +825,30 @@ Query:
Plan:
Query:
SELECT rcv_file_id, rcv_file_entity_id, prefix_path
FROM rcv_files
WHERE created_at < ?
Plan:
SCAN rcv_files
Query:
SELECT rcv_file_id, rcv_file_entity_id, prefix_path
FROM rcv_files
WHERE deleted = 1
Plan:
SCAN rcv_files
Query:
SELECT rcv_file_id, rcv_file_entity_id, tmp_path
FROM rcv_files
WHERE status IN (?,?) AND tmp_path IS NOT NULL
Plan:
SEARCH rcv_files USING INDEX idx_rcv_files_status_created_at (status=?)
Query:
SELECT rcv_replica_id, rcv_replica_key
FROM snd_file_chunk_replica_recipients
@@ -764,6 +857,30 @@ Query:
Plan:
SEARCH snd_file_chunk_replica_recipients USING INDEX idx_snd_file_chunk_replica_recipients_snd_file_chunk_replica_id (snd_file_chunk_replica_id=?)
Query:
SELECT snd_file_id, snd_file_entity_id, prefix_path
FROM snd_files
WHERE created_at < ?
Plan:
SCAN snd_files
Query:
SELECT snd_file_id, snd_file_entity_id, prefix_path
FROM snd_files
WHERE deleted = 1
Plan:
SCAN snd_files
Query:
SELECT snd_file_id, snd_file_entity_id, prefix_path
FROM snd_files
WHERE status IN (?,?) AND prefix_path IS NOT NULL
Plan:
SEARCH snd_files USING INDEX idx_snd_files_status_created_at (status=?)
Query:
UPDATE client_services
SET service_id = ?
@@ -1068,11 +1185,15 @@ Query: DELETE FROM conn_invitations WHERE invitation_id = ?
Plan:
SEARCH conn_invitations USING PRIMARY KEY (invitation_id=?)
Query: DELETE FROM conn_invitations WHERE service_request = 1 AND created_at < ?
Plan:
SCAN conn_invitations
Query: DELETE FROM connections WHERE conn_id = ?
Plan:
SEARCH connections USING PRIMARY KEY (conn_id=?)
SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?)
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_hash (conn_id=?)
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_conn_id (conn_id=?)
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=?)
SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_conn_id_internal_id (conn_id=?)
SEARCH commands USING COVERING INDEX idx_commands_conn_id (conn_id=?)
@@ -1087,7 +1208,7 @@ Query: DELETE FROM connections WHERE user_id = 2
Plan:
SEARCH connections USING COVERING INDEX idx_connections_user (user_id=?)
SEARCH address_ratchet_keys USING COVERING INDEX idx_address_ratchet_keys (conn_id=?)
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_hash (conn_id=?)
SEARCH processed_ratchet_key_hashes USING COVERING INDEX idx_processed_ratchet_key_hashes_conn_id (conn_id=?)
SEARCH encrypted_rcv_message_hashes USING COVERING INDEX idx_encrypted_rcv_message_hashes_hash (conn_id=?)
SEARCH snd_message_deliveries USING COVERING INDEX idx_snd_message_deliveries_conn_id_internal_id (conn_id=?)
SEARCH commands USING COVERING INDEX idx_commands_conn_id (conn_id=?)
@@ -1098,6 +1219,10 @@ SEARCH messages USING COVERING INDEX idx_messages_conn_id (conn_id=?)
SEARCH snd_queues USING COVERING INDEX idx_snd_queue_id (conn_id=?)
SEARCH rcv_queues USING COVERING INDEX idx_rcv_queue_id (conn_id=?)
Query: DELETE FROM deleted_snd_chunk_replicas WHERE created_at < ?
Plan:
SEARCH deleted_snd_chunk_replicas USING COVERING INDEX idx_deleted_snd_chunk_replicas_pending (created_at<?)
Query: DELETE FROM deleted_snd_chunk_replicas WHERE deleted_snd_chunk_replica_id = ?
Plan:
SEARCH deleted_snd_chunk_replicas USING INTEGER PRIMARY KEY (rowid=?)
@@ -1117,6 +1242,14 @@ Query: DELETE FROM ntf_subscriptions WHERE conn_id = ?
Plan:
SEARCH ntf_subscriptions USING PRIMARY KEY (conn_id=?)
Query: DELETE FROM ntf_tokens_to_delete WHERE created_at < ?
Plan:
SCAN ntf_tokens_to_delete
Query: DELETE FROM processed_ratchet_key_hashes WHERE conn_id = ? AND processed_ratchet_key_hash_id <= ? AND created_at < ?
Plan:
SEARCH processed_ratchet_key_hashes USING INDEX idx_processed_ratchet_key_hashes_conn_id (conn_id=? AND processed_ratchet_key_hash_id<?)
Query: DELETE FROM ratchets WHERE conn_id = ?
Plan:
SEARCH ratchets USING PRIMARY KEY (conn_id=?)
@@ -1242,6 +1375,18 @@ Query: SELECT conn_id FROM connections WHERE deleted = 0
Plan:
SEARCH connections USING COVERING INDEX idx_connections_deleted (deleted=?)
Query: SELECT conn_id FROM connections WHERE deleted = ?
Plan:
SEARCH connections USING COVERING INDEX idx_connections_deleted (deleted=?)
Query: SELECT conn_id FROM connections WHERE deleted_at_wait_delivery IS NOT NULL
Plan:
SCAN connections
Query: SELECT conn_id FROM connections WHERE service_request_expires_at < ? AND deleted = 0 AND deleted_at_wait_delivery IS NULL
Plan:
SEARCH connections USING INDEX idx_connections_deleted (deleted=?)
Query: SELECT conn_id FROM connections WHERE user_id = ?
Plan:
SEARCH connections USING COVERING INDEX idx_connections_user (user_id=?)
@@ -1250,6 +1395,10 @@ Query: SELECT conn_id, rc_verify_code_ad, rc_verify_code_pq, CASE WHEN rc_verify
Plan:
SEARCH ratchets USING PRIMARY KEY (conn_id=?)
Query: SELECT count(1) FROM commands
Plan:
SCAN commands USING COVERING INDEX idx_commands_conn_id
Query: SELECT count(1) FROM connections
Plan:
SCAN connections USING COVERING INDEX idx_connections_deleted
@@ -1270,10 +1419,6 @@ Query: SELECT deleted FROM snd_files WHERE snd_file_id = ?
Plan:
SEARCH snd_files USING INTEGER PRIMARY KEY (rowid=?)
Query: SELECT header_key, msg_n, msg_key FROM skipped_messages WHERE conn_id = ?
Plan:
SEARCH skipped_messages USING INDEX idx_skipped_messages_conn_id (conn_id=?)
Query: SELECT key_hash FROM servers WHERE host = ? AND port = ?
Plan:
SEARCH servers USING PRIMARY KEY (host=? AND port=?)
@@ -1290,6 +1435,10 @@ Query: SELECT ratchet_state, x3dh_pub_key_1, x3dh_pub_key_2, pq_pub_kem FROM rat
Plan:
SEARCH ratchets USING PRIMARY KEY (conn_id=?)
Query: SELECT ratchet_sync_state FROM connections WHERE conn_id = ?
Plan:
SEARCH connections USING PRIMARY KEY (conn_id=?)
Query: SELECT rcv_file_id FROM rcv_files WHERE rcv_file_entity_id = ?
Plan:
SEARCH rcv_files USING COVERING INDEX sqlite_autoindex_rcv_files_1 (rcv_file_entity_id=?)
@@ -1310,6 +1459,10 @@ Query: SELECT rcv_queue_id, broker_id FROM rcv_messages WHERE conn_id = ? AND in
Plan:
SEARCH rcv_messages USING PRIMARY KEY (conn_id=?)
Query: SELECT skipped_message_id, header_key, msg_n, msg_key FROM skipped_messages WHERE conn_id = ? ORDER BY skipped_message_id DESC LIMIT ?
Plan:
SEARCH skipped_messages USING INDEX idx_skipped_messages_conn_id (conn_id=?)
Query: SELECT snd_file_id FROM snd_files WHERE snd_file_entity_id = ?
Plan:
SEARCH snd_files USING COVERING INDEX idx_snd_files_snd_file_entity_id (snd_file_entity_id=?)
@@ -1835,11 +1835,15 @@ SEARCH contacts USING INTEGER PRIMARY KEY (rowid=?)
Query:
UPDATE group_members
SET contact_id = ?, updated_at = ?
WHERE user_id = ? AND group_id = ? AND group_member_id = ?
SET contact_id = ?, local_display_name = ?, contact_profile_id = ?, updated_at = ?
WHERE (user_id = ? AND group_id = ? AND group_member_id = ?) OR contact_id = ?
Plan:
MULTI-INDEX OR
INDEX 1
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
INDEX 2
SEARCH group_members USING INDEX idx_group_members_contact_id (contact_id=?)
Query:
UPDATE group_members
@@ -7282,6 +7286,10 @@ Query: SELECT COUNT(1) FROM chat_items WHERE user_id = ? AND group_id = ? AND ms
Plan:
SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id=? AND msg_content_tag=? AND item_deleted=? AND item_sent=?)
Query: SELECT COUNT(1) FROM commands WHERE command_function = 'set_short_link'
Plan:
SCAN commands
Query: SELECT COUNT(1) FROM contacts WHERE user_id = ? AND chat_item_ttl > 0
Plan:
SEARCH contacts USING INDEX idx_contacts_chat_ts (user_id=?)
@@ -7382,11 +7390,7 @@ Query: SELECT chat_item_id FROM chat_items WHERE group_scope_tag = 'member_suppo
Plan:
SCAN chat_items
Query: SELECT chat_item_id FROM chat_items WHERE item_sent = 0 AND item_text LIKE '!2 SB-8H8V3-PF8CV-PQMA2-A54M3!%'
Plan:
SCAN chat_items
Query: SELECT chat_item_id FROM chat_items WHERE item_sent = 0 AND item_text LIKE '!2 SB-Y14GX-Z83KW-0E3PK-DEN7X!%'
Query: SELECT chat_item_id FROM chat_items WHERE item_sent = 0 AND item_text LIKE ? || '%'
Plan:
SCAN chat_items
@@ -7684,6 +7688,10 @@ Query: SELECT member_xcontact_id, member_welcome_shared_msg_id FROM group_member
Plan:
SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?)
Query: SELECT next_wake_at, badge_purchase_id FROM badge_purchases
Plan:
SCAN badge_purchases
Query: SELECT note_folder_id FROM note_folders WHERE user_id = ?
Plan:
SEARCH note_folders USING COVERING INDEX note_folders_user_id (user_id=?)
@@ -7756,6 +7764,10 @@ Query: SELECT shared_msg_id FROM chat_items WHERE shared_msg_id IS NOT NULL ORDE
Plan:
SCAN chat_items
Query: SELECT short_descr FROM contact_profiles WHERE display_name = ? LIMIT 1
Plan:
SEARCH contact_profiles USING INDEX contact_profiles_index (display_name=?)
Query: SELECT should_sync FROM connections_sync WHERE connections_sync_id = 1
Plan:
SEARCH connections_sync USING INTEGER PRIMARY KEY (rowid=?)