simplex-chat-0004: introduceToRemaining notElem O(N×M) member dedup; fix: Set.notMember O(log N)
This commit is contained in:
parent
eb534f0944
commit
d9ca5b236f
12 changed files with 1006 additions and 0 deletions
|
|
@ -0,0 +1,51 @@
|
|||
# Defect: simplex-chat-0004 — introduceToRemaining `notElem` O(N×M) member dedup
|
||||
|
||||
**Project:** simplex-chat
|
||||
**File:** `src/Simplex/Chat/Library/Internal.hs`
|
||||
**Function:** `introduceToRemaining` / `introduceMemP`
|
||||
**Complexity:** O(|members| × |introducedGMIds|) per member join event
|
||||
**Severity:** MEDIUM
|
||||
**Fix:** Convert `introducedGMIds :: [GroupMemberId]` to `Set GroupMemberId` before filter
|
||||
|
||||
## Pattern
|
||||
|
||||
```haskell
|
||||
-- DEFECT: O(|members| × |introducedGMIds|)
|
||||
introduceToRemaining vr user gInfo m = do
|
||||
(members, introducedGMIds) <-
|
||||
withStore' $ \db -> (,) <$> getGroupMembers db vr user gInfo
|
||||
<*> getIntroducedGroupMemberIds db m
|
||||
let recipients = filter (introduceMemP introducedGMIds) members
|
||||
...
|
||||
where
|
||||
introduceMemP introducedGMIds mem =
|
||||
memberCurrent mem
|
||||
&& groupMemberId' mem `notElem` introducedGMIds -- O(|introducedGMIds|) scan
|
||||
&& groupMemberId' mem /= groupMemberId' m
|
||||
```
|
||||
|
||||
## Fix
|
||||
|
||||
```haskell
|
||||
-- FIX: O(|members| × log|introducedGMIds|)
|
||||
import qualified Data.Set as S
|
||||
|
||||
introduceToRemaining vr user gInfo m = do
|
||||
(members, introducedGMIds) <-
|
||||
withStore' $ \db -> (,) <$> getGroupMembers db vr user gInfo
|
||||
<*> getIntroducedGroupMemberIds db m
|
||||
let introducedSet = S.fromList introducedGMIds
|
||||
recipients = filter (introduceMemP introducedSet) members
|
||||
...
|
||||
where
|
||||
introduceMemP introducedSet mem =
|
||||
memberCurrent mem
|
||||
&& groupMemberId' mem `S.notMember` introducedSet -- O(log N)
|
||||
&& groupMemberId' mem /= groupMemberId' m
|
||||
```
|
||||
|
||||
## Impact
|
||||
|
||||
In a group with N members where M introductions have been made:
|
||||
- Per new member join: O(N×M) → O(N log M)
|
||||
- At N=1000 members, M=500 introductions: 500,000 → 9,000 comparisons (**55× overhead**)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# UNDF: (pending)
|
||||
--- a/src/Simplex/Chat/Library/Internal.hs
|
||||
+++ b/src/Simplex/Chat/Library/Internal.hs
|
||||
@@ -30,6 +30,7 @@ import Control.Monad.Except
|
||||
import Data.Bifunctor (bimap, first)
|
||||
import Data.Fixed (Fixed (..))
|
||||
import Data.List (find, foldl', mapAccumL, partition)
|
||||
+import qualified Data.Set as S
|
||||
import Data.Map.Strict (Map)
|
||||
import qualified Data.Map.Strict as M
|
||||
...
|
||||
@@ -1064,9 +1064,11 @@ introduceToRemaining vr user gInfo m = do
|
||||
(members, introducedGMIds) <-
|
||||
withStore' $ \db -> (,) <$> getGroupMembers db vr user gInfo <*> getIntroducedGroupMemberIds db m
|
||||
- let recipients = filter (introduceMemP introducedGMIds) members
|
||||
+ let introducedSet = S.fromList introducedGMIds
|
||||
+ recipients = filter (introduceMemP introducedSet) members
|
||||
introduceMember vr user gInfo m recipients Nothing
|
||||
where
|
||||
- introduceMemP introducedGMIds mem =
|
||||
+ introduceMemP introducedSet mem =
|
||||
memberCurrent mem
|
||||
- && groupMemberId' mem `notElem` introducedGMIds
|
||||
+ && groupMemberId' mem `S.notMember` introducedSet
|
||||
&& groupMemberId' mem /= groupMemberId' m
|
||||
Loading…
Add table
Add a link
Reference in a new issue