-
Notifications
You must be signed in to change notification settings - Fork 549
improvement(client): improved internal submitMessage, reSubmit, rollback, and submitSignal type-safety #24319
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
base: main
Are you sure you want to change the base?
Conversation
…ack, and submitSignal type-safety In Legacy API: - Add type parameter to `IEnvelope` for `contents` to support stricter typing where desired. - Deprecate `FluidDataStoreRuntime.submitMessage` as not required per `IFluidDataStoreChannel`. `IFluidParentContext` (which is base interface for `IFluidDataStoreContext`) should always be used to access `submitMessage` functionality. - Remove unused parameters from mock `submitMessage` and `submitSignal` methods. Internally: - Replace `any` in `ISignalEnvelope` with `unknown`. - Relocate `FluidDataStoreMessage` to runtime-definitions. Eventually this can serve as basis for message types for DataStores. - In `datastore`, create `LocalFluidDataStoreRuntimeMessage` representing the two known messages for DataStores. - `FluidDataStoreContext` requires `OutboundFluidDataStoreMessage` for stricter `reSubmit` and `rollback` methods. - Create `IFluidRootParentContext` for the transition from DataStore to `ContainerRuntime` and use explicit types in `ContainerRuntime` and `ChannelCollection`. - In `ChannelCollection` restore some old method names with strict typing that internal callers must conform to. The `IFluidDataStoreChannel` portions that were not required by current use were refactored to new `ComposableChannelCollection` class. - Replace `wrapContext` with `formParentContext` that handles both `IFluidParentContext` and `IFluidRootParentContext` by requiring the specific `submitMessage` and `submitSignal` functions - Within `ContainerRuntime`: - Update `submitMessage` to expect specific messages - Update `submitSignal` to expect new `AddressedSignalEnvelope` that is specific subset of `ISignalEnvelope` and is exactly `IEnvelope<ISignalEnvelope["contents"]>` - Fixup tests to use more realistic data (as required by stricter typing). Potential Future Improvements: - Change {@link IFluidDataStoreChannel} and {@link IFluidParentContext}, to have a generic specifying `T extends FluidDataStoreMessage` and uses `T["type"]` and `T["content"]` to qualify message related methods, preferably where `submitMessage`, `reSubmit`, and `rollback` have overloads to ensure callers pair values correctly. - A further improvement would be to reshape `submitMessage`, `reSubmit`, and `rollback` to accept `T` as `message` parameter instead of `type` and `content` parameters that are hard to convince TypeScript must be paired in implementations.
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.
Copilot reviewed 26 out of 27 changed files in this pull request and generated no comments.
Files not reviewed (1)
- packages/runtime/datastore/package.json: Language not supported
Comments suppressed due to low confidence (2)
packages/framework/attributor/src/runtimeAttributorDataStoreChannel.ts:189
- The reSubmit method in this attributor channel now has no parameters, which deviates from the rest of the interface. Consider adding documentation or a comment clarifying that reSubmit is intentionally not supported in this context.
public reSubmit(): void {
packages/runtime/container-runtime/src/channelCollection.ts:724
- [nitpick] The naming of 'reSubmitContainerMessage' (and the corresponding 'rollbackDataStoreOp') differs from the standard naming conventions used in other parts of the code. Consider standardizing the naming across modules for easier maintainability.
public readonly reSubmitContainerMessage = (
/** | ||
* @deprecated No implementation required and will be removed in 2.50. | ||
*/ | ||
public submitMessage(type: DataStoreMessageType, content: any, localOpMetadata: unknown) { |
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.
This deprecation is not required. Just seemed like it might be stale and a clean-up opportunity.
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.
Yeah I've noticed this isn't used too I think. The asymmetry with submitSignal caught my eye, but I guess the point is, op semantics are internal to the FluidDataStore/DDS implementations, and DataStore authors don't get to generate them manually. Signals is an open protocol that the runtime makes available to DataStore authors.
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.
(hence submitMessage
not existing on IFluidDataStoreRuntime
)
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.
Haven't looked at the actual change in detail, but a few doc comments in the meantime
…jected types for `submitSignal` and `submitMessage`. Additionally, deprecate `MockFluidDataStoreRuntime.submitMessage`.
🔗 No broken links found! ✅ Your attention to detail is admirable. linkcheck output
|
packages/runtime/runtime-definitions/api-report/runtime-definitions.legacy.alpha.api.md
Show resolved
Hide resolved
* @internal | ||
* | ||
* @privateRemarks | ||
* Future use opportunity: |
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.
Sounds nice!
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.
FYI @anthony-murphy
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.
Ok I'm talking out of both sides of my mouth - in the other comment I talked down adding these generics, didn't immediately connect the dots to this comment til now. I totally get the structure here and it's obviously appealing in one sense, but I'm concerned about how it will feel in practice. Maybe too many generics? Other downsides I'm not thinking of?
* | ||
* @privateRemarks | ||
* `type` parameter's type of `DataStoreMessageType` is a covariance exception | ||
* over `string` that `IFluidDataStoreChannel` specifies. (`unreachableCase` |
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.
Some thoughts on this - It's strictly true, but only messages that were submitted from here will come back for resubmit. So IMO it is a legitimate, stable and robust way to type this. I don't think the warning about unreachableCase being hit over time is warranted.
I believe it's correct that IFluidDataStoreChannel
should specify string
here, since another implementation could have its own schema. And we haven't wanted to parametrize everything, it leads to quite a soup of generics and doesn't provide much value in practice IMO.
* | ||
* @privateRemarks | ||
* `type` parameter's type of `DataStoreMessageType` is a covariance exception | ||
* over `string` that `IFluidDataStoreChannel` specifies. |
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.
Same as above. If you're going to call this out, please also point out that the only values passed in here at runtime are those passed out via this.dataStoreContext.submitMessage
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.
I do believe that is the intent, yet the contract (typing) doesn't say that is definitely the case. It is up to the caller to respect that and not have bugs. I am pretty sure there are improvements that can be made to provide safety and help with readability.
But, yes, we can note the intent in comments in the meantime.
* Type of the op, within the ContainerRuntime's domain | ||
*/ | ||
type: TType; | ||
// eslint-disable-next-line @typescript-eslint/no-namespace |
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.
Talk to me about namespace
. What does it do, why is the linter saying don't do it, and why is it so rare in our codebase?
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.
I can't get answers to why the linter says don't do it. It may stem from when there was a bunch of churn in the space with ESM modules and confusing terminology.
This was more useful earlier when I was exploring things and it looked like this might leak out. Can and probably should just roll this back.
} | ||
|
||
type SubmitKeys = "submitMessage" | "submitSignal"; |
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.
Nit: Either export as internal and use in IFluidRootParentContext
or inline these in both places.
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.
Hmm. There is a separation of concerns here that happens to coincide because of this utility and makes it look like they must be the same. In practice there can be other differences between the two interfaces.
I'll want to look at what is going on with IFluidParentContextPrivate
. Might be some changes per that.
export function formParentContext<T extends IFluidParentContext | IFluidRootParentContext>( | ||
context: Omit<IFluidParentContext & IFluidRootParentContext, SubmitKeys>, | ||
overrides: Pick<T, SubmitKeys>, | ||
): Omit<IFluidParentContext & IFluidRootParentContext, SubmitKeys> & Pick<T, SubmitKeys> { |
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.
Here's a good time to plug this PR of mine I never pushed through #23694 Review comments welcome even as it's closed, I do expect to reopen it.
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.
Mutable should probably go next to Deep/ShallowReadonly in core-interfaces
* @internal | ||
* @privateRemarks Exposed per ChannelCollection testing and API extractor request | ||
*/ | ||
export interface IFluidRootParentContext |
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.
You'll need to rationalize splitting this out with the new IFluidParentContextPrivate
added by #24465
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.
Maybe "reconcile" is what you meant. Yeah, probably having this first (proposed way before) would have been good. This goes a lot further on splitting out the nested datastore support and might simplify or clarify things.
@@ -708,11 +711,11 @@ export class ContainerRuntime | |||
extends TypedEventEmitter<IContainerRuntimeEvents> | |||
implements | |||
IContainerRuntime, | |||
Omit<IFluidRootParentContext, "submitSignal">, |
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.
Toss a comment here explaining why the mismatch? (it's a type mismatch that leads you to omit this right?)
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.
Can do. It is not a type mismatch. ContainerRuntime
doesn't need to implement any of IFluidRootParentContext
. It does implement most of it so that it can hand itself over to the formParentContext
function to make an IFluidRootParentContext
. So, the utility here is to force type checking on those members in the class versus there being a type failure when this
is given to formParentContext
.
@@ -4461,7 +4464,7 @@ export class ContainerRuntime | |||
case ContainerMessageType.Alias: { | |||
// For Operations, call resubmitDataStoreOp which will find the right store |
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.
Out of date / out of context comment?
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.
Perhaps a little out of date. I suspect this case only handled FluidDataStoreOp to begin with and was expanded for Attach and Alias later.
|
||
Implementing `FluidDataStoreRuntime.submitMessage` is not required per `IFluidDataStoreChannel` and is now deprecated on `FluidDataStoreRuntime` and corresponding `MockFluidDataStoreRuntime`. | ||
|
||
See [issue #24406](https://github.com/microsoft/FluidFramework/issues/24406) for details and alternatives. |
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.
That issue mentions:
IFluidParentContext (the base interface for IFluidDataStoreContext) should always be used to access submitMessage functionality.
That may be true for inside FluidDataStoreRuntime
class, but as for the public API to submit an op, I think the comment should just be - "Submitting arbitrary messages is not supported"
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.
Is FluidDataStoreRuntime
a class customers are expected to derive from? I don't think Pages does and I see that we do internally in at least two places.
Feel free edit the issue.
We should seal the class if no one should derive from it. Then it will be easier to blanketly say don't call.
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.
LGTM overall! I'll take another pass once you're all merged up and have made the changes you're mentioning in comments
In Legacy API:
IEnvelope
forcontents
to support stricter typing where desired.submitMessage
onFluidDataStoreRuntime and
MockFluidDataStoreRuntimeas not required per
IFluidDataStoreChannel.
IFluidParentContext(which is base interface for
IFluidDataStoreContext) should always be used to access
submitMessage` functionality.submitMessage
andsubmitSignal
methods.Internally:
any
inISignalEnvelope
withunknown
.FluidDataStoreMessage
to runtime-definitions. Eventually this can serve as basis for message types for DataStores.datastore
, createLocalFluidDataStoreRuntimeMessage
representing the two known messages for DataStores.FluidDataStoreContext
requiresOutboundFluidDataStoreMessage
for stricterreSubmit
androllback
methods.IFluidRootParentContext
for the transition from DataStore toContainerRuntime
and use explicit types inContainerRuntime
andChannelCollection
.ChannelCollection
restore some old method names with strict typing that internal callers must conform to. TheIFluidDataStoreChannel
portions that were not required by current use were refactored to newComposableChannelCollection
class.wrapContext
withformParentContext
that handles bothIFluidParentContext
andIFluidRootParentContext
by requiring the specificsubmitMessage
andsubmitSignal
functionsContainerRuntime
:submitMessage
to expect specific messagessubmitSignal
to expect newAddressedSignalEnvelope
that is specific subset ofISignalEnvelope
and is exactlyIEnvelope<ISignalEnvelope["contents"]>
Potential Future Improvements:
T extends FluidDataStoreMessage
and usesT["type"]
andT["content"]
to qualify message related methods, preferably wheresubmitMessage
,reSubmit
, androllback
have overloads to ensure callers pair values correctly.submitMessage
,reSubmit
, androllback
to acceptT
asmessage
parameter instead oftype
andcontent
parameters that are hard to convince TypeScript must be paired in implementations.