Skip to content
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

Approaches for state migration with Reaqtive #130

Open
NickDarvey opened this issue Jan 21, 2023 · 1 comment
Open

Approaches for state migration with Reaqtive #130

NickDarvey opened this issue Jan 21, 2023 · 1 comment

Comments

@NickDarvey
Copy link

Say I'm building a service which will start and stop now and then so I want to make use of Reaqtive's stateful goodness.

I have v1 of my application with an Reaqtive expression like:

let work = xss.Concat()

Later, I realize improve on my application and release a v2 containing a Reaqtive expression:

let work = xss.Concat().Take(5)

If I went ahead and tried to restore my state now, I would get an exception like:

An unhandled exception of type 'System.InvalidOperationException' occurred in Reaqtive.Core.dll
Failed to restore state for artifact 'Reaqtive.Operators.Take`1+_[System.String]' with expected tag 'rc:Take'. State tag is 'rcx:Concat'.

What are some approaches I could take to migrate the state I've stored with LoadState/SaveState?

@idg10
Copy link
Contributor

idg10 commented Jul 7, 2023

The way Reaqtor deals with this is that subscriptions themselves are persistent. When you subscribe to a source in Reaqtor, the subscription records its defining expression. So when a query engine restarts, all the subscriptions are restarted based on their defining expressions.

So if you changed the code that creates the subscriptions, that doesn't affect any subscriptions that have already been persisted.

What you've shown implies a different approach. If you build up the query yourself, and presumably you do actually subscribe to it at some point, otherwise there are no actual instances of the operators to persist, if you save the state without also persisting the query expression, then you now have to define some other way of ensuring that you create the right query structure.

My first suggestion would be: don't. Just do what Reaqtor does, and persist the subscription's defining expression.

However, if you really can't do that then all you can really do is persist some sort of version number along with the state. (There are facilities in the codebase for doing this. Individual operators put a version number in their persisted data so that they can provide a path forward if they ever change their format). When you deserialize you'd need to detect when the old version was serialised, and recreate something with the original structure, or perhaps, if it's even possible, work out some sort of migration strategy.

Migration is going to require dedicated code because each case will be different. There's not any general way to move from one query to another. In this case you could reconstitute the old query exactly as is, and then append the Take(5) on the end of it. But that would get messy when it came to serializing it again because you'd have to serialize it in two parts: the old query without the Take and then the Take applied after it. (Perhaps you could do something to walk the subscription tree and somehow stitch things together but it's not instantly obvious to me whether that's even possible.)

But the example you've given isn't even very complex. What would be the right thing to do if you have some complex query that partitions data, splits them into multiple nested observables, performs per-nested-stream processing, then recombines them in some way. If you decide you want to use a different structure of query to achieve your goals, then there might not be any reasonable way to convert.

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

No branches or pull requests

2 participants