Skip to content

refactor(metatable): replace parallel cache arrays with containers.Map#112

Merged
ehennestad merged 18 commits into
devfrom
metatable-cache-map
Jun 8, 2026
Merged

refactor(metatable): replace parallel cache arrays with containers.Map#112
ehennestad merged 18 commits into
devfrom
metatable-cache-map

Conversation

@ehennestad

@ehennestad ehennestad commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Replaces the MetaObjectCache array + MetaObjectCacheMembers cell-array pair with a single containers.Map keyed by normalised ID, making the ID→object relationship the sole source of truth
  • Removes updateMetaObjectCacheMembers (the sync step that was required after every mutation) and findCachedMetaObjectIndex (linear ID scan) — both made redundant by Map operations
  • Removes the attachCacheEvictionListener / onMetaObjectDestroyed machinery in favour of lazy eviction: an invalid cached handle is detected and evicted on the next getMetaObjects call rather than via an ObjectBeingDestroyed callback

Why

The parallel-array design required an explicit sync step (updateMetaObjectCacheMembers) after every cache mutation. This was called redundantly — once per cached object — inside the refreshMetaObjectCacheVariableAdded loop. With a Map the cache is self-consistent by construction, and all lookups are O(1) instead of O(N) linear scans.

Test plan

  • runtests('nansen.unittest.metadata.MetaTableCacheInvalidationTest') — 18 tests, all passing with no changes to test code

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown

Test Results

265 tests   265 ✅  27s ⏱️
 18 suites    0 💤
  1 files      0 ❌

Results for commit c6d6a14.

♻️ This comment has been updated with latest results.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Refactors MetaTable meta-object caching from parallel arrays to a single containers.Map, removing sync/listener machinery and switching to lazy eviction of stale handles.

Changes:

  • Replace MetaObjectCache/MetaObjectCacheMembers with a containers.Map keyed by normalized IDs.
  • Remove cache-member syncing and eviction listeners; detect & evict invalid cached handles on next access.
  • Update the invalidation test to reflect lazy-eviction behavior.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

File Description
code/+nansen/+metadata/@MetaTable/MetaTable.m Replaces cache storage with containers.Map, rewrites cache refresh/invalidation logic, and implements lazy eviction in getMetaObjects.
tests/+nansen/+unittest/+metadata/MetaTableCacheInvalidationTest.m Updates a test name/doc to match lazy eviction and removes a warning-free delete step.
.github/badges/tests.svg Updates the “tests passed” badge text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread code/+nansen/+metadata/@MetaTable/MetaTable.m Outdated
@ehennestad ehennestad force-pushed the metatable-cache-map branch from fa4b7c4 to 41a3ab1 Compare June 6, 2026 08:09
ehennestad and others added 14 commits June 7, 2026 21:59
MetaObjectCache and MetaObjectCacheMembers were a parallel-array pair
requiring an explicit sync step (updateMetaObjectCacheMembers) after
every mutation. Replace with a single containers.Map keyed by normalised
ID so the ID→object relationship is the sole source of truth.

Key changes:
- MetaObjectCache is now a containers.Map (char → any), initialised in
  the constructor; MetaObjectCacheMembers property removed entirely.
- updateMetaObjectCacheMembers, findCachedMetaObjectIndex removed — Map
  lookups (isKey/Map(id)/remove) replace the old linear-scan + rebuild.
- Eviction listener (attachCacheEvictionListener / onMetaObjectDestroyed)
  removed in favour of lazy eviction: an invalid cached handle is evicted
  on the next getMetaObjects call rather than via an ObjectBeingDestroyed
  callback. Behaviour observed by callers is identical.
- invalidateMetaObjectCache, all refreshMetaObjectCache* methods, and
  resetMetaObjectCache rewritten to use Map operations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename testDestroyingCachedObjectEvictsItFromCache to
  testDestroyingCachedObjectRebuildsCacheOnNextAccess and add a comment
  describing the lazy eviction mechanism: an invalidated cached handle is
  detected and evicted on the next getMetaObjects call, not at delete time.
- Remove the delete(cachedItem) assertion from testRoutineCacheFlowsAreWarningFree.
  That line guarded against the ObjectBeingDestroyed listener emitting
  CacheMemberMissing; the listener was removed in the containers.Map
  refactor so the assertion was vacuous.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous wording said status indicates "if an object was created",
which was inaccurate — cached objects also get status = true. The
semantics are: true = valid object obtained (from cache or newly
constructed); false = construction failed for that row.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- assignTableVariableAdded → applyAddTableVariable (clearer verb phrase)
- assignTableVariableRemoved inlined into removeTableVariable (single
  call site, no reason for the indirection)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pattern

Consistent event-responder naming across all four private helpers:
  refreshMetaObjectCacheProperty    → refreshCacheOnPropertyChanged
  refreshMetaObjectCacheRows        → refreshCacheOnRowsChanged
  refreshMetaObjectCacheVariableAdded    → refreshCacheOnVariableAdded
  refreshMetaObjectCacheVariableRemoved  → refreshCacheOnVariableRemoved

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…helpers

- removeTableVariable now delegates to master when called on a dummy,
  fixing a bug where removal was never propagated (unlike addTableVariable
  which already did this correctly)
- Extract applyAddTableVariableToMaster and applyRemoveTableVariableFromMaster
  as private methods; addTableVariable and removeTableVariable now hold
  the full local logic directly
- Remove applyAddTableVariable (logic inlined into addTableVariable)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds testEditCellColumnRefreshesCachedProperty to guard against the bug
fixed in the previous commit: refreshCacheOnPropertyChanged was passing
the raw cell wrapper ({value}) to refreshProperty instead of the
unwrapped value.

- Add Tags = {} property to RefreshableTestItem (untyped; holds whatever
  the table produces after unwrapping the cell-in-cell convention)
- Add Tags column to createRefreshableItemMetaTable using the scalar-cell
  storage convention ({{'tag1'}; ...})
- Test verifies cachedItem.Tags equals the char 'newtag' (not {'newtag'})
  after editEntries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CellUtilTest covers isWrapped and unWrap directly — flat cells,
nested scalar cells, multi-level nesting, and non-cell inputs.

MetaTableCellColumnTest covers the assignEntries cell-column path:
plain char input gets wrapped, already-wrapped input passes through,
and doubly-wrapped input is unwrapped before storage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@ehennestad ehennestad force-pushed the metatable-cache-map branch from 3392c15 to 9aa2dd6 Compare June 7, 2026 20:00
@ehennestad ehennestad merged commit 40a7b32 into dev Jun 8, 2026
5 checks passed
@ehennestad ehennestad deleted the metatable-cache-map branch June 8, 2026 08:58
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.

2 participants