-
Notifications
You must be signed in to change notification settings - Fork 531
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
App serializer POC #21410
App serializer POC #21410
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks pretty good!
One thing I had envisioned is the sharedCell being more build-in, or at least have a pre-defined key name dictated by us, that the application would need to use in order for it to be considered the serialized app data. And should we be showcasing the markdown from the sharedCell being accessible in the summaries API response, or a different route? The point of having that sharedCell is that we would know where it is and how to process it, so we could access the contents server-side without further user code, right?
examples/client-logger/app-insights-logger/src/components/useAppSerializer.ts
Outdated
Show resolved
Hide resolved
* | ||
* | ||
*/ | ||
gitCommit: ICommitDetails; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a final version, I'd hesitate to expose this in this form. The fact we use git for the summaries should be a non-exposed implementation detail, I think.
* - "https://graph.microsoft.com/types/counter" | ||
* - "https://graph.microsoft.com/types/map" | ||
* - "https://graph.microsoft.com/types/mergeTree" | ||
* - "https://graph.microsoft.com/types/directory" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before putting these in a final version, I'd check with the team if we should be treating them as permanently immutable across the lifetime of a document. I don't think we have plans to change them, but not sure if we should be extending their use, particularly to something server-side.
examples/client-logger/app-insights-logger/src/components/useAppSerializer.ts
Outdated
Show resolved
Hide resolved
+1. But what you have here makes sense as an initial prototype. One additional comment: I don't think we would want the cell to live under the root shared-map. A single root map isn't how all apps will model their data. It would probably be a bit better to have a recommended pattern for the cell being a separate item in the container schema (so next to the map, rather than under it, in this case). |
This PR has been automatically marked as stale because it has had no activity for 60 days. It will be closed if no further activity occurs within 8 days of this comment. Thank you for your contributions to Fluid Framework! |
Description
Proof of concept for a client side app serializer. This falls under the umbrella of solving the problem of having client derived data that can be accessible at rest/server side.
Understanding the code
The App serializer consists of three main components:
1. The
Dependency
classThis class encapsulates a given object (DDS) or primitive and is responsible for:
getValue()
method.method. Checking if a dependency's value change is "qualified" is used downstream in the next class,
DependencyChangeEffect`.**Note that the
Dependency
class does not actually track the previous and next values.2. The
DependencyChangeEffect
classThis class takes an array of
Dependency
's and one callback (aka the effect) as its constructor arguments. When thetrigger()
method of theDependencyChangeEffect
is fired, the class will check if at least one of its providedDependency
classes has changed and uses the optional ,Dependency.qualifier(prev,next)
of each to determine if the change matters/is qualified. If atleast oneDependency
has a change that is qualified then theDependencyChangeEffect
class will fire the provided callback and save the results of said callback to an internal member variable.In practice for app serialization, we provide one or more DDS's whose values we depend on each other to produce a single part of the serialized state of your app as
Dependency
's to theDependencyChangeEffect
class and provide a callback/effect that produces a part of the serialized state of our app using said DDS's. Finally, we then listen to theonChange()
handlers of each DDS and call theDependencyChangeEffect.trigger()
method as needed which will then reproduce the piece of serialized state. Great!, now we have independently generated, serialized pieces of our application state. that are only regenerated as needed.**Note that unlike the
Dependency
class, theDependencyChangeEffect
class in charge of tracking the previous values of each of its dependencies.3. The
AppSerializer
classThis class takes an array of
DependencyChangeEffect
classes, an interval time and a SharedCell DDS handle. We established above that eachDependencyChangeEffect
in this case should be used for producing independently generated, serialized pieces of our application state. TheAppSerializer
will then be in charge of combining the pieces serialized state into one string and saving them to a SharedCell DDS on a given millisecond interval, if and only if one or more of the pieces has actually changed.**Note The
AppSerializer
class can efficiently check if one of itsDependencyChangeEffect
's has changed on each interval with simple number comparisons thanks to theeffectResultIteration
of eachDependencyChangeEffect
which is a monotonically increasing number that goes up by 1 each time aDependencyChangeEffect
produces a new result.Future design considerations
Example of the app:
Setting up the app serializer:
You'll see that there are a few parts to creating the different segments in our app serializer.
First we have each
serializationSegment
which is effectively an individual piece of serialized state that depends on some number of dependencies.A segment is represented by the
DependencyChangeEffect
class which takes an array ofDependency
's and aneffect
function. WhenDependencyChangeEffect.trigger()
is called, the class will determine if itsDependency
's changed and use the optionalqualifier
of eachDependency
to determine if the effect should be triggered. We hook into each DDS's.on(<event>)
function to determine how/when wetrigger()
eachDependencyChangeEffect
.In the example below, we trigger the
DependencyChangeEffect
on every single change to the DDS, but in practice we can use algorithms such asdebounce
fromlodash
to make the trigger execution more performant.Finally, once we assemble our
serializationSegment
's, we can pass them to theAppSerializer
class which will assemble the segments into a single serialized string on a defined interval and save it to a shared cell. In the future, we could consider saving the data as json that can be reassembled into one of many output formatsexamples/client-logger/app-insights-logger/src/components/App.tsx
Console logging the serialization:
Pulling the data from the api:
-See
server/routerlicious/packages/tinylicious/src/routes/summaries/getLatestSummaryApi.ts