Skip to content

Conversation

@danisharora099
Copy link
Collaborator

@danisharora099 danisharora099 commented Nov 28, 2025

Problem / Description

ILocalHistory was designed as an array-like interface (push, find, some, slice) which required O(N × M) array scans for dependency checking, which resulted in expensive lookup times

Solution

  • Refactor ILocalHistory to implement a domain-specific interface:
export interface ILocalHistory {
  readonly size: number;
  addMessages(...messages: ContentMessage[]): void;
  hasMessage(messageId: string): boolean;
  getMessage(messageId: string): ContentMessage | undefined;
  getRecentMessages(count: number): ContentMessage[];
  getAllMessages(): ContentMessage[];
  findMissingDependencies(entries: HistoryEntry[]): HistoryEntry[];
}
  • Removed generic array-like methods
  • Using a hashmap (messageIndex) behind ILocalHistory for O(1) lookups for missing dependency
  • Result is faster dependency checks: O(N+M)

Notes


Checklist

  • Code changes are covered by unit tests.
  • Code changes are covered by e2e tests, if applicable.
  • Dogfooding has been performed, if feasible.
  • A test version has been published, if required.
  • All CI checks pass successfully.

…istentHistory` to use composition over inheritance for history management.
…ifying history storage initialization to default to `localStorage`
…d refactor `MemLocalHistory` to support optional persistent storage.
@github-actions
Copy link

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
Waku node 96.29 KB (0%) 2 s (0%) 929 ms (-9.2% 🔽) 2.9 s
Waku Simple Light Node 147.65 KB (0%) 3 s (0%) 1.2 s (+59.45% 🔺) 4.2 s
ECIES encryption 22.62 KB (0%) 453 ms (0%) 275 ms (-4.33% 🔽) 727 ms
Symmetric encryption 22 KB (0%) 440 ms (0%) 197 ms (+19.52% 🔺) 637 ms
DNS discovery 52.17 KB (0%) 1.1 s (0%) 466 ms (+15.55% 🔺) 1.6 s
Peer Exchange discovery 52.91 KB (0%) 1.1 s (0%) 496 ms (-14.15% 🔽) 1.6 s
Peer Cache Discovery 46.64 KB (0%) 933 ms (0%) 666 ms (+30.43% 🔺) 1.6 s
Privacy preserving protocols 77.31 KB (0%) 1.6 s (0%) 885 ms (+19.89% 🔺) 2.5 s
Waku Filter 79.75 KB (0%) 1.6 s (0%) 1.2 s (+57.37% 🔺) 2.8 s
Waku LightPush 77.96 KB (0%) 1.6 s (0%) 1.2 s (+84.28% 🔺) 2.8 s
History retrieval protocols 83.75 KB (0%) 1.7 s (0%) 867 ms (-2.6% 🔽) 2.6 s
Deterministic Message Hashing 28.98 KB (0%) 580 ms (0%) 566 ms (+23.95% 🔺) 1.2 s

@fryorcraken
Copy link
Collaborator

This PR should go before the persistent storage one. Because here, you are refining the interface to implement, and in persistent storage, you are adding a second implementation.

* If an array of items longer than `maxLength` is pushed, dropping will happen
* at next push.
*/
export interface ILocalHistory {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MessageChannel is the one to define the interface, hence this should be in message_channel.ts.

addMessages(...messages: ContentMessage[]): void;
hasMessage(messageId: string): boolean;
getMessage(messageId: string): ContentMessage | undefined;
getRecentMessages(count: number): ContentMessage[];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the expectation is for this to be ordered? if so, I'd add it to the name.

}

public get length(): number {
public get size(): number {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Comment on lines 45 to +46
private items: ContentMessage[] = [];
private messageIndex: Map<string, ContentMessage> = new Map();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private items: ContentMessage[] = [];
private messageIndex: Map<string, ContentMessage> = new Map();
private orderedMessageArray: ContentMessage[] = [];
private messageMap: Map<string, ContentMessage> = new Map();

I don't think a Map qualify as an index because it cotnains both keys (index) and values (not a ref)

// Remove duplicates by messageId while maintaining order
this.items = _.uniqBy(combinedItems, "messageId");

this.rebuildIndex();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are rebuilding the index, and then you are removing items from the array, and then you are removing the removed items from the index.

I think there is a better way to do this.

@danisharora099 danisharora099 force-pushed the feat/persistent_history branch from 75e8e12 to e396c3b Compare December 10, 2025 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants