New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow EntityCache to function at any point in the transformer pipeline #32
Comments
Thanks for giving this so much thought, Paul. What you are suggesting above looks like it would work well for us and would solve the one roadblock we hit attempting to use Siesta and Realm in an integrated way. We would still want to use Realm because we need database functions beyond persistence. We need to query objects and relationships and perform other business logic (calculations, summaries, rollups) on our data while offline (disconnected from the server). I'm really excited to hear you are considering making a change like this for v1. Many Thanks. |
Thanks for giving it a look, Annica. Since writing this up, I also started wondering if I shouldn’t just make all In that case, would the change I’m proposing here help you? Maybe. You could do something like this: …where the Siesta → Realm flow is unidirectional: the Realm cache’s In this scheme, Realm is only there for queries and rollups; the app doesn’t rely on Realm to make network requests work offline. When you make a request, Siesta first asks the Realm cache, but that one always says “Not it!” It then checks with the JSON cache, which might actually return something. Does this make any sense? It seems like it would work if Realm is just a cache too, but it would fall apart if you’re trying to update Realm locally and then push those changes back to the server. There’s a deeper problem here I’ve been trying to avoid: I don’t want to make Siesta try to handle bidirectional sync. That pushes too deep into API-specific behavior. Right now, Siesta is strictly a cache, and the server remains the ultimate source of truth. It’s up to the app to manage updates: what they look like, when they happen, what to do with client-side state if they fail. That seems like the right line to draw. However, I think a lot of Siesta client projects (including yours) are going to push up against it, and I don’t have a clear answer. Thus my interest in your case. |
I totally agree that Siesta should NOT try to be responsible for syncing data or state back up to the server. We were not expecting or even hoping for that - we are writing our own code manage that process. So I support the line you are drawing there. However I do have concerns about the evolution you are proposing to your original suggestion. BUT... they may be specific to our requirements and beyond your scope. Because of the security requirements in our industry (Health Care) we cannot save any data to disk that is not encrypted. Any data we persist to disk will need to be inside an encrypted Realm instance so we wouldn't be able to make use of the File-based EntityCache you propose above (unless we can encrypt it somehow). I was depending on us being able to re-populate the EntityCache from the models in our encrypted Realm Instance - which means we would need to save the URL's in the database AND have a way to read the data back into the EntityCache from the data stored in Realm. And now that I think about it... I guess this is a problem that existed in your original suggestion as well. Any caching we do will need to read/write to and from an encrypted store - which is one of the two reasons I was trying point your PersistantCache at Realm in the first place. The second reason, of course, is the additional processing we need to do on the data we plan to persist: object and relationship queries and rollups. Hmmmm.... |
I think the need for encrypted caching is widespread. Right now, of course, Siesta doesn’t even provide an implementation of To that end:
|
And an addendum to that: even though you’re not relying on Siesta to handle the sync problem (yay!), I’m very interested to know about any roadblocks you hit in the course of doing that that do fall squarely on Siesta’s side of the line. |
That works for me, Paul. We will plan to write an encrypted implementation of EntityCache for now, and follow the roadmap you laid out above.... and wait patiently for v 1.0 with the solution to our Realm problem. Again... many thanks. |
Hey there, was wondering if there has been any progress with the Realm integration and if/when to expect to get a sneak peak? Thanks. |
@jyounus Well, I don’t have a paying client for Siesta improvements at the moment, so it’s (alas) a spare time project, taking a backseat to the teaching and other urgent things. But this feature is definitely high on my list! I will post to this issue when I have something testable. |
No worries, I was just wondering if this got anywhere. Appreciate all the work and efforts that have and are being put into this project! :) |
After some head-scratching and light prototyping, I have a design proposal for this. BackgroundThe challenge here is that the transformer pipeline currently is just an unstructured (and uninspectable) array, and depends on users writing configuration so that transformers get added in the order they should run, with no opportunity to insert or replace specific ones. This design was already stretched a little thin, but this issue clearly breaks it: clients need to be able to say “add cache X at point Y in the pipeline” where Y is not necessarily the end. We therefore need some way to say “point Y in the pipeline.” Because transformers are usually structs, using instance equality is not an option. Because they are often closures wrapped in a single I originally considered letting clients specify an optional ID when adding a transformer. However, this turns out to be fairly awkward at the point of use, and is also brittle. ProposalConceptThe big API change is that the pipeline would have a sequence of identifiable named stages: Clients can customize the set of stages and their order. Each stage has zero or more transformers: …and a cache: During response processing, each cache receives an entity for writing after all the stage’s transformers have run. When reinflating a new resource, Siesta checks each cache in turn starting with the end of the pipeline. If there is a cache hit, Siesta takes whatever the cache returned and runs it through the pipeline’s subsequent stages. APIThe opaque service.configure {
$0.config.pipeline[.parsing].add(SwiftyJSONTransformer, contentTypes: ["*/json"])
$0.config.pipeline[.cleanup].add(GithubErrorMessageExtractor())
} The ability to identify transformers by stage creates the new ability to remove or replace specific transformers in the pipeline. This is frequently request feature for model transformers. To this end, service.configureTransformer("/users/*") {
User(json: $0.content)
}
// ...is shorthand for:
service.configureTransformer("/users/*", atStage: .model, replaceExisting: true) {
User(json: $0.content)
}
// ...which is shorthand for:
service.configure("/users/*") {
$0.config.pipeline[.model].removeTransformers()
$0.config.pipeline[.model].add(
ResponseContentTransformer(
User(json: $0.content)))
} @MPiccinato, I think this would nicely solve #47? service.configureTransformer("/products/*") { Product(...) }
service.configureTransformer("/products/filters") { Filter(...) } // overrides line above Each stage has an optional cache: service.configure {
$0.config.pipeline[.rawData].cache = encryptedFileCache
$0.config.pipeline[.model].cache = realmCache
} Clients can create custom stages with an arbitrary order: service.configure {
$0.config.pipeline.order = [.munging, .twiddling, .blending, .baking]
} My tentative list of default stages:
Thanks everyone for your patience on this. I have a precious window of time right now to move fast on implementing this, if we can get to a satisfactory design quickly. Please send your reactions and nitpicks! |
I’ve pushed a draft implementation of this to the structured-pipeline branch. It’s a work in progress, but I think it’s testable if you want to experiment with it. |
Does anyone have made some kind of working entity cache with realm ? Or a realm extension ? |
EntityCache currently must store and return only the values at the very end of the transformer pipeline.
This works well when the end result of the pipeline is an easily serializable type such as text or JSON:
However, it’s problematic when the transform pipeline produces a model, which can be difficult for an
EntityCache
to store and retrieve:Attempts to solve this problem by keeping models in a database quickly become problematic:
EntityCache
API is not designed to work with databases. (For example, it wants to pass cached entities across threads, which Realm doesn’t like.)Suppose, however, that an
EntityCache
could insert itself at any point in the transformer pipeline and not just the end. In other words, this is currently the only supported structure:…but suppose the pipeline instead supported this:
When there is a cache hit, the pipeline would pick up immediately after the cache's position in the pipeline. An
EntityCache
could then work only with its preferred data type:…or even:
This would require some hard thought in the plumbing, but seems to make sense. @annicaburns, would this solve the problems you were having with
EntityCache
? Would you still want to use Realm even with a mechanism like this in place?The text was updated successfully, but these errors were encountered: