Skip to content

Fluid Framework v2.11.0 (minor)

Latest
Compare
Choose a tag to compare
@github-actions github-actions released this 03 Dec 01:03
· 58 commits to main since this release
a6b1b73

Contents

✨ New Features

Synchronous Child Datastore Creation (#23143)

Overview

This feature introduces a new pattern for creating datastores synchronously within the Fluid Framework. It allows for the synchronous creation of a child datastore from an existing datastore, provided that the child datastore is available synchronously via the existing datastore's registry and that the child's factory supports synchronous creation. This method also ensures strong typing for the consumer.

In this context, "child" refers specifically to the organization of factories and registries, not to any hierarchical or hosting relationship between datastores. The parent datastore does not control the runtime behaviors of the child datastore beyond its creation.

The synchronous creation of child datastores enhances the flexibility of datastore management within the Fluid Framework. It ensures type safety and provides a different way to manage datastores within a container. However, it is important to consider the overhead associated with datastores, as they are stored, summarized, garbage collected, loaded, and referenced independently. This overhead should be justified by the scenario's requirements.

Datastores offer increased capabilities, such as the ability to reference them via handles, allowing multiple references to exist and enabling those references to be moved, swapped, or changed. Additionally, datastores are garbage collected after becoming unreferenced, which can simplify final cleanup across clients. This is in contrast to subdirectories in a shared directory, which do not have native capabilities for referencing or garbage collection but are very low overhead to create.

Synchronous creation relies on both the factory and the datastore to support it. This means that asynchronous operations, such as resolving handles, some browser API calls, consensus-based operations, or other asynchronous tasks, cannot be performed during the creation flow. Therefore, synchronous child datastore creation is best limited to scenarios where the existing asynchronous process cannot be used, such as when a new datastore must be created in direct response to synchronous user input.

Key Benefits

  • Synchronous Creation: Allows for the immediate creation of child datastores without waiting for asynchronous operations.
  • Strong Typing: Ensures type safety and better developer experience by leveraging TypeScript's type system.

Use Cases

Example 1: Creating a Child Datastore

In this example, we demonstrate how to support creating a child datastore synchronously from a parent datastore.

/**
 * This is the parent DataObject, which is also a datastore. It has a
 * synchronous method to create child datastores, which could be called
 * in response to synchronous user input, like a key press.
 */
class ParentDataObject extends DataObject {
  createChild(name: string): ChildDataStore {
    assert(
      this.context.createChildDataStore !== undefined,
      "this.context.createChildDataStore",
    );

    const { entrypoint } = this.context.createChildDataStore(
      ChildDataStoreFactory.instance,
    );
    const dir = this.root.createSubDirectory("children");
    dir.set(name, entrypoint.handle);
    entrypoint.setProperty("childValue", name);

    return entrypoint;
  }

  getChild(name: string): IFluidHandle<ChildDataStore> | undefined {
    const dir = this.root.getSubDirectory("children");
    return dir?.get<IFluidHandle<ChildDataStore>>(name);
  }
}

For a complete example see the following test: https://github.com/microsoft/FluidFramework/blob/main/packages/test/local-server-tests/src/test/synchronousDataStoreCreation.spec.ts

Change details

Commit: 3426b43

Affected packages:

  • @fluidframework/container-runtime
  • @fluidframework/runtime-definitions

⬆️ Table of contents

Presence-related events now support the off event deregistration pattern (#23196)

Event subscriptions within @fluidframework/presence may now use off to deregister event listeners, including initial listeners provided to Notifications.

Some type names have shifted within the API though no consumers are expected to be using those types directly. The most visible rename is NotificationSubscribable to NotificationListenable. Other shifts are to use types now exported through @fluidframework/core-interfaces where the most notable is ISubscribable that is now Listenable.

Change details

Commit: f7be965

Affected packages:

  • @fluidframework/presence

⬆️ Table of contents

Presence updates are now grouped and throttled (#23075)

Presence updates are grouped together and throttled to prevent flooding the network with messages when presence values are rapidly updated. This means the presence infrastructure will not immediately broadcast updates but will broadcast them after a configurable delay.

The allowableUpdateLatencyMs property configures how long a local update may be delayed under normal circumstances, enabling grouping with other updates. The default allowableUpdateLatencyMs is 60 milliseconds but may be (1) specified during configuration of a States Workspace or Value Manager and/or (2) updated later using the controls member of a Workspace or Value Manager. The States Workspace configuration applies when a Value Manager does not have its own setting.

Notifications are never queued; they effectively always have an allowableUpdateLatencyMs of 0. However, they may be grouped with other updates that were already queued.

Note that due to throttling, clients receiving updates may not see updates for all values set by another. For example, with Latest*ValueManagers, the only value sent is the value at the time the outgoing grouped message is sent. Previous values set by the client will not be broadcast or seen by other clients.

Example

You can configure the grouping and throttling behavior using the allowableUpdateLatencyMs property as in the following example:

// Create and configure a states workspace
const stateWorkspace = presence.getStates(
  "app:v1states",
  {
    // This value manager has an allowable latency of 100ms.
    position: Latest({ x: 0, y: 0 }, { allowableUpdateLatencyMs: 100 }),
    // This value manager uses the workspace default allowable latency of 60ms.
    count: Latest({ num: 0 }),
  },
  // Set the default allowable latency for all value managers in this workspace to 200ms,
  // overriding the default value of 60ms.
  { allowableUpdateLatencyMs: 200 },
);

// Temporarily set count updates to send as soon as possible.
const countState = stateWorkspace.props.count;
countState.controls.allowableUpdateLatencyMs = 0;
countState.local = { num: 5000 };

// Reset the update latency to the workspace default of 60ms.
countState.controls.allowableUpdateLatencyMs = undefined;

Change details

Commit: abde76d

Affected packages:

  • @fluidframework/presence

⬆️ Table of contents

🌳 SharedTree DDS Changes

✨ New! Alpha APIs for indexing (#22491)

SharedTree now supports indexing via two new APIs, createSimpleTreeIndex and createIdentifierIndex.

createSimpleTreeIndex is used to create a SimpleTreeIndex which indexes nodes based on their schema. Depending on the schema, the user specifies which field to key the node on.

The following example indexes IndexableParents and IndexableChilds and returns the first node of a particular key:

function isStringKey(key: TreeIndexKey): key is string {
  return typeof key === "string";
}

const index = createSimpleTreeIndex(
  view,
  new Map([
    [IndexableParent, parentKey],
    [IndexableChild, childKey],
  ]),
  (nodes) => nodes[0],
  isStringKey,
  [IndexableParent, IndexableChild],
);

createIdentifierIndex is used to create an IdentifierIndex which provides an efficient way to retrieve nodes using the node identifier.

Example:

const identifierIndex = createIdentifierIndex(view);
const node = identifierIndex.get("node12345");

Change details

Commit: cd95357

Affected packages:

  • fluid-framework

⬆️ Table of contents

Providing unused properties in object literals for building empty ObjectNodes no longer compiles (#23162)

ObjectNodes with no fields will now emit a compiler error if constructed from an object literal with fields. This matches the behavior of non-empty ObjectNodes which already gave errors when unexpected properties were provided.

class A extends schemaFactory.object("A", {}) {}
const a = new A({ thisDoesNotExist: 5 }); // This now errors.

Change details

Commit: dc3c300

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Revertible objects can now be cloned using RevertibleAlpha.clone() (#23044)

The DisposableRevertible interface has been replaced with RevertibleAlpha. The new RevertibleAlpha interface extends Revertible and includes a clone(branch: TreeBranch) method to facilitate cloning a Revertible to a specified target branch. The source branch where the RevertibleAlpha was created must share revision logs with the target branch where the RevertibleAlpha is being cloned. If this condition is not met, the operation will throw an error.

Change details

Commit: 5abfa01

Affected packages:

  • fluid-framework
  • @fluidframework/tree

⬆️ Table of contents

Other Changes

API clarifications for devtools packages (#23165)

APIs that were never intended for direct consumer use have been marked as @system. These are:

  • HasContainerKey

APIs that were not intended to be extended by consumers have been marked as @sealed. These are:

  • ContainerDevtoolsProps
  • DevtoolsProps
  • HasContainerKey
  • IDevtools

Additionally, interface properties have been marked as readonly.

Change details

Commit: cea34d1

Affected packages:

  • @fluidframework/devtools
  • @fluidframework/devtools-core

⬆️ Table of contents

🛠️ Start Building Today!

Please continue to engage with us on GitHub Discussion and Issue pages as you adopt Fluid Framework!