Skip to content

perf: address Stellar Wave issues #100, #101, #102, #103#162

Open
Mirabel64 wants to merge 2 commits into
UnityChainxx:mainfrom
Mirabel64:perf/stellar-wave-perf-fixes
Open

perf: address Stellar Wave issues #100, #101, #102, #103#162
Mirabel64 wants to merge 2 commits into
UnityChainxx:mainfrom
Mirabel64:perf/stellar-wave-perf-fixes

Conversation

@Mirabel64

Copy link
Copy Markdown

Closes #100
Closes #101
Closes #102
Closes #103

  • NFTCard renders heavy gradients every render; not memoized #103 NFTCard: wrap export in React.memo and lift the rarity gradient lookup to module scope so its identity is stable across renders (no new function per render). Default shallow compare on the single nft prop is sufficient.

  • puzzleReviewService.delay() called on every read, even bulk #102 puzzleReviewService: extract _applyReviewStatusSync so the bulk path no longer stacks an await this.delay() per item (100 items were ~50s; now <1ms). The bulk response now also exposes a skipped: string[] field for IDs that didn't match a known review so callers can detect partial failures.

  • useGameStore persists on every action — including warms #101 useGameStore: pass localStorage writes through a debounced storage adapter (createThrottledStorage, 150ms window) so the score/auth/NFT actions don't trigger a synchronous write on every set(). partialize restricts persistence to the durable progress fields (user, completedPuzzles, completedDifficulties, currentDifficulty, currentPuzzleIndex, score, nfts). An SSR-safe storage shim prevents crash during Next.js static generation. Bumped version to 1.

  • Analytics in-memory Map cannot survive multi-replica #100 analytics: the in-memory Map in AnalyticsService is no longer the only source of truth. With REDIS_URL configured, every recordPuzzleSolveAsync updates the in-memory mirror synchronously and writes the increment atomically to Redis via HINCRBY pipelines keyed by puzzleId/userId+puzzleId, with index sets for enumeration. Reads go through new async variants (getMostSolvedPuzzlesAsync / getAverageSolveTimeAsync / getUserPuzzleStatsAsync) which query Redis first and fall back to the in-memory mirror on connect or command failures. The public sync methods are retained for tests and seedData. The AnalyticsController now routes through the async service methods so responses stay consistent across replicas.

Analytics service test extended with deterministic aggregate + no- double-increment coverage over the in-memory fallback path.

Closes #100, #101, #102, #103

Summary

Changes

Testing

Related Issues

…nityChainxx#102, UnityChainxx#103

- UnityChainxx#103 NFTCard: wrap export in React.memo and lift the rarity
  gradient lookup to module scope so its identity is stable across
  renders (no new function per render). Default shallow compare on
  the single nft prop is sufficient.

- UnityChainxx#102 puzzleReviewService: extract _applyReviewStatusSync so the
  bulk path no longer stacks an await this.delay() per item (100
  items were ~50s; now <1ms). The bulk response now also exposes a
  skipped: string[] field for IDs that didn't match a known review
  so callers can detect partial failures.

- UnityChainxx#101 useGameStore: pass localStorage writes through a debounced
  storage adapter (createThrottledStorage, 150ms window) so the
  score/auth/NFT actions don't trigger a synchronous write on
  every set(). partialize restricts persistence to the durable
  progress fields (user, completedPuzzles, completedDifficulties,
  currentDifficulty, currentPuzzleIndex, score, nfts). An SSR-safe
  storage shim prevents crash during Next.js static generation.
  Bumped version to 1.

- UnityChainxx#100 analytics: the in-memory Map in AnalyticsService is no
  longer the only source of truth. With REDIS_URL configured, every
  recordPuzzleSolveAsync updates the in-memory mirror synchronously
  and writes the increment atomically to Redis via HINCRBY pipelines
  keyed by puzzleId/userId+puzzleId, with index sets for enumeration.
  Reads go through new async variants (getMostSolvedPuzzlesAsync /
  getAverageSolveTimeAsync / getUserPuzzleStatsAsync) which query
  Redis first and fall back to the in-memory mirror on connect or
  command failures. The public sync methods are retained for tests
  and seedData. The AnalyticsController now routes through the async
  service methods so responses stay consistent across replicas.

Analytics service test extended with deterministic aggregate + no-
double-increment coverage over the in-memory fallback path.

Closes UnityChainxx#100, UnityChainxx#101, UnityChainxx#102, UnityChainxx#103
@drips-wave

drips-wave Bot commented Jul 25, 2026

Copy link
Copy Markdown

@Mirabel64 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant