Azure / Azure/azure-functions-durable-js

Entities do not support non-JSON serializable states

Open
#437 1 comment 0 reactions 0 assignees View on GitHub
bug Needs: Investigation :mag:
Dominant language
TypeScript
Stars
142
Forks
66
Avg merge
3d 19h
Merged PRs (30d)
4

Description

If you try to set a durable entity state to a non-serializable object, for example a rich class, the methods of that class would not be available and your entity/orchestration would fail with an error. The simplest example of this is the `ShoppingEntity`/`ShoppingOrchestration` example in the `samples/` directory:

`ShoppingEntity/index.ts`:

```TS
import * as df from "durable-functions";
import { CartItem, ShoppingCart, Operations } from "../Shopping/data";

module.exports = df.entity(function (context) {
const cart = context.df.getState(() => new ShoppingCart());

switch (context.df.operationName) {
case Operations.ADD_ITEM:
const cartItem = context.df.getInput();
cart.addItem(cartItem);
break;
case Operations.REMOVE_ITEM:
const itemToRemove = context.df.getInput();
cart.remoteItem(itemToRemove);
break;
case Operations.GET_VALUE:
const value = cart.getCartValue();
context.df.return(value);
break;
}

context.df.setState(cart);
});
```

`ShoppingOrchestration/index.ts`:

```TS
import * as df from "durable-functions";
import { items, Operations } from "../Shopping/data";

module.exports = df.orchestrator(function* (context) {
const entityId = new df.EntityId("ShoppingEntity", "shoppingCart");

yield context.df.callEntity(entityId, Operations.ADD_ITEM, {
itemId: items[0].itemId,
quantity: 1,
});
yield context.df.callEntity(entityId, Operations.ADD_ITEM, {
itemId: items[2].itemId,
quantity: 2,
});
yield context.df.callEntity(entityId, Operations.ADD_ITEM, {
itemId: items[1].itemId,
quantity: 10,
});

const cartValue: number = yield context.df.callEntity(
entityId,
Operations.GET_VALUE
);

console.log(cartValue);
});
```
`ShoppingCart` class definition:

```TS
export class ShoppingCart {
#items: CartItem[];

constructor() {
this.#items = [];
}

public addItem(item: CartItem): void {
this.#items.push(item);
}

public remoteItem(itemId: string): void {
const index = this.#items.findIndex((item) => item.itemId === itemId);
this.#items = this.#items.slice(index, index + 1);
}

public getCartValue(): number {
return this.#items.reduce(
(value, item) =>
value + item.quantity * items.find((i) => i.itemId === item.itemId).price,
0
);
}
}
```

Attempting to call the above `ShoppingOrchestration` orchestration actually doesn't work, and fails because `cart.addItem()` isn't a function (it is lost after serialization/deserialization). For obvious reasons, entity states can only be JSON-serializable values, since they have to be serialized/deserialized to be written to storage.

What I would recommend:
1. Make sure we clearly document this limitation about entities
2. Remove the shopping sample from our repo.

Alternatively, we could try to think about how we could make a rich class use-case work. For example, we could have a way for a user to provide a class constructor to the state initializer, and call that constructor every time we retrieve the state. This may still not cover every use-case though. More investigation and design would be required to see what makes sense here and if it's worth it.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Contributor guide

Open the contributing guide

Research direction

Start by reviewing the entity limitation described in the issue and the examples in samples/ShoppingEntity/index.ts, samples/ShoppingOrchestration/index.ts, and the Shopping data definitions. Document that entity state must be JSON-serializable and remove the shopping sample if it is still present. Rich class state support remains an alternative requiring further investigation and design.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend-api-design, documentation
Issue type
Documentation
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.