Skip to content

Implement composite cache#765

Open
mxiao-cll wants to merge 4 commits into
mainfrom
OPDATA-6402
Open

Implement composite cache#765
mxiao-cll wants to merge 4 commits into
mainfrom
OPDATA-6402

Conversation

@mxiao-cll
Copy link
Copy Markdown
Contributor

@mxiao-cll mxiao-cll commented May 7, 2026

Background: https://smartcontract-it.atlassian.net/browse/OPDATA-6402

Commit 1:

  • Split ResponseCache into ResponseCache (base) and SimpleResponseCache (implementation)
  • Move constructor into base
  • Move writeTTL into base
  • Move cache entry creation logic into base
  • Everything else is in implementation

Commit 2:

  • Re-structure cache so that we can use different transport name for cache key & meta in EA response field
    • If we have a composite transport holding http and ws transport, we still want http and ws transport to write into the same cache key, however the EA response should reflect which transport is used to generate this answer
  • Implement compare cache
    • It holds a responseCache which actually writes into cache
    • It also takes a shouldUpdate function to determine if new entry should be written into cache or not
    • Instead of always loading from cache, it maintains a list of recently written entry so we do a local comparison first
    • It uses responseCache's transport for cache key but it's own transport for EA response meta
  • Implement composite transport
    • Initialize all the child transport with the same compare cache
    • Simply forward each operation to the individual child transport and let cache do the work

Commit 3:

  • Add docs

How to use this: https://github.com/smartcontractkit/external-adapters-js/pull/4939/changes

@mxiao-cll mxiao-cll requested a review from a team as a code owner May 7, 2026 23:34
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

👋 mxiao-cll, thanks for creating this pull request!

To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team.

Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

NPM Publishing labels 🏷️

🟢 This PR has valid version labels and will cause a patch bump.

Copy link
Copy Markdown
Contributor

@alejoberardino alejoberardino left a comment

Choose a reason for hiding this comment

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

@mxiao-cll from the ticket for this change I agree with your challenge arguments wholeheartedly, would be good to see the intended effect of this change in the concrete impl you have in mind even as a draft

@mxiao-cll mxiao-cll requested a review from alejoberardino May 8, 2026 03:32
@mxiao-cll
Copy link
Copy Markdown
Contributor Author

mxiao-cll commented May 8, 2026

@mxiao-cll from the ticket for this change I agree with your challenge arguments wholeheartedly, would be good to see the intended effect of this change in the concrete impl you have in mind even as a draft

Big PR here you go and example usage here: https://github.com/smartcontractkit/external-adapters-js/pull/4939/changes

@mxiao-cll mxiao-cll force-pushed the OPDATA-6402 branch 2 times, most recently from ca83238 to a414c9e Compare May 8, 2026 13:12
@mxiao-cll mxiao-cll changed the title Create a base class for response cache Implement composite cache May 8, 2026
@mxiao-cll mxiao-cll added patch and removed none labels May 8, 2026
Comment on lines +73 to +75
async writeEntries() {
throw new Error('Use write instead for CompareResponseCache')
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this one seems to be internal, can probably just be a method of the simple class and not of this one instead of being part of the interface

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The problem is that the compareCache takes the interface as input instead of the simple class

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

seems more correct to me to have this expect an extended interface (or even the class) than to have this fail at runtime

Comment thread src/transports/composite.ts
})

test.serial(
'composite transport merges child writes using shouldUpdate when run under an adapter',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

would be nice to add a couple more test cases for the unhappy paths, e.g. having one transport fail to produce a value, requests coming with a specific transport request, etc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added 2 tests, not sure about "requests coming with a specific transport request", that doesn't seem to be related to the new transport added.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unless we took it out the framework supports specifying which transport to use when requesting (useful when the EA for example has a crypto endpoint and we know in advance a particular asset pair only exists in the rest transport and not the ws). I don't think this is a feature we can / should remove, and for safety I'd probably have the framework make sure that cases like these are supported, working, and not allowing for multiple instances of the transports that could cause problems (e.g. multiple ws connections, rate limiting failures to the provider)

Comment on lines +73 to +75
async writeEntries() {
throw new Error('Use write instead for CompareResponseCache')
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

seems more correct to me to have this expect an extended interface (or even the class) than to have this fail at runtime

Comment on lines +48 to +57
await Promise.all(
Object.entries(this.config.transports).map(([name, transport]) =>
transport.initialize(
{ ...dependencies, responseCache: compareCache },
adapterSettings,
endpointName,
name,
),
),
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One thing that came to mind when looking at the example usage PR in the ea monorepo (thanks for that btw, made this easier to reason even if very simple) is that we could mistakenly create multiple instances of the same adapter and initialize both, and potentially do unexpected things from the EAs like creating multiple connections to providers when we only intended/support one.

We need to add something on the framework side to check that if we're using a composite transport for a particular endpoint, either the underlying transports are the same as the composite one, we take the composite one and register the underlying ones automatically, etc.

})

test.serial(
'composite transport merges child writes using shouldUpdate when run under an adapter',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unless we took it out the framework supports specifying which transport to use when requesting (useful when the EA for example has a crypto endpoint and we know in advance a particular asset pair only exists in the rest transport and not the ws). I don't think this is a feature we can / should remove, and for safety I'd probably have the framework make sure that cases like these are supported, working, and not allowing for multiple instances of the transports that could cause problems (e.g. multiple ws connections, rate limiting failures to the provider)

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants