Persisting data across fetches using Continuation - antipattern?

Does anyone know the practical limits of how much can or should be stored in a Continuation? Docs say Continuation is “not designed for persisting large amounts of data between executions.”

Full context:

  • I’m working on a pack for Copper CRM that pulls in Opportunities (some CRMs would call these Deals) via a sync table.
  • Each Opportunity can be assigned in Copper to someone on your team, identified in the API response as an assignee_id.
  • Coda allows you to have a column try to auto-match to a Coda user based on their email address. So I need to grab a list of assignee_ids and their corresponding email addresses from Copper. Ideally I only do this once, at the beginning of the sync, and then I can match this against any assignee_ids I encounter through multiple continuations.
  • My current solution is to store this list of Copper users in the Continuation object (type definition doesn’t allow arrays, so I’m using the slightly janky approach of using the assignee_ids as keys and email addresses as values). I’m not sure what a realistic upper bound is on number of users… a couple dozen? (Any more than that and I’m going to hit all kinds of other limitations like the 10k row limit)

I know I could re-fetch this list with every page of results, but that seems wasteful (unless Coda’s caching means it’s not?). Does this seem like a bad idea?

Coda is caching fetcher responses and you can set up your own TTL:

https://coda.github.io/packs-sdk/guides/advanced/fetcher/#caching

So I guess you should use that and not include a payload within the continuation object.

I also don’t think there’s any arbitrary limit on the length of that object, it’s just that the chunk of data is sent to-and-fro on each paginated sync request.

TL;DR: It’s as much of an antipattern as sending chunks of data through Cookies. Most often it’s not a good idea unless you absolutely have to.

When writing that documentation I asked the engineers about a size limit for the continuation object. They don’t enforce one today, but they may in the future, hence a degree of vagueness there. To @Paul_Danyliuk’s point because of the caching at the Fetcher level you don’t need to worry about making the same request in each sync execution, and it removes the risk with storing a lot of data in the continuation object.

Thanks guys.

Just to confirm, is caching independent of the verb? Are POSTs cached the same way as GETs?

Edit: found documentation here explaining that, by default, GETs are cached for 5 minutes. Can anyone confirm whether POSTs will respect cacheTtlSecs?

(Many of the non-mutative calls I need to make are POST in this API; I think they made this choice because the endpoints accept search parameters in the request body)

Update: it seems like cacheTtlSecs isn’t being respected on POST requests even when explicitly set.

I could be misinterpreting my results, but I refactored a sync formula in a way that called a POST Fetch on every row instead of every page, with cacheTtlSecs set to 300. It used to be reasonably performant but now times out after 60 seconds.

Can you confirm this is by design @Eric_Koleda ?

Also, is there any way to confirm in the logs whether a response is fresh or cached? Do the requests visible in the log represent true requests only or cached ones as well?

Edit: I suppose there may also be a performance hit when requesting a cached response compared to retrieving from a variable - true? Any rough sense of how much of a hit?

@Nick_HE - Great point, that’s something I didn’t make clear in the docs: only GET requests are cached. Since POST requests are meant to have a side effect (creating something on a server) they are never cached.

There is probably a small overhead when reading something from the HTTP cache, but it should be trivial and I wouldn’t design around it.

Thanks Eric. Hopefully not too many APIs are designed like this one (because I agree, a search term in the body of a request designed only to get results should be a GET, not a POST).