docs icon indicating copy to clipboard operation
docs copied to clipboard

Incorrect outbox metadata example for Python

Open jseguillon opened this issue 1 year ago • 3 comments

Describe the issue

Example for outbox in python seems to be errored. It assumes we can, via python SDK, do:

  await client.state.transaction(DAPR_STORE_NAME, ops);

but:

== APP ==     c.state.save(
== APP == AttributeError: 'DaprClient' object has no attribute 'state'

Indeed python SDK does not provide this state attribute.

So I tried:

    c.save_state(
        store_name=DAPR_STORE_NAME,
        key="test",
        value='{ "say": "hello world from outbox-metadata" }',
        state_metadata={  "outbox.projection": "true" },
        metadata=(("outbox.projection", "true"),)
    )

But despite my efforts in injecting metadatas, I could not see any outbox message in my outbox topic.

Only way I found to get a message in outbox topic is to use transactions:

    transaction_operation = TransactionalStateOperation(
        operation_type=TransactionOperationType.upsert,
        key="1",
        data='{ "say": "hello world from outbox transaction" }',
    )

    c.execute_state_transaction(
        store_name=DAPR_STORE_NAME,
        operations=[transaction_operation],
        transactional_metadata={ 'datacontenttype': 'application/json'},
        metadata= (('datacontenttype', 'application/json'),)
    )

I made this project where you can find full code and logs: https://github.com/jseguillon/dapr-outbox-python

URL of the docs

https://github.com/dapr/docs/blob/v1.14/daprdocs/content/en/developing-applications/building-blocks/state-management/howto-outbox.md?plain=1#L205

Expected content

A working Python example for outbox with metadata like example for others langages

Screenshots

metadata:

Image

transaction:

Image

Additional context

jseguillon avatar Jan 30 '25 20:01 jseguillon

@jseguillon Appreciate you taking the time to raise this issue. Yes you are correct , the example is wrong. Can I suggest

  1. That the sample in docs https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-outbox/#shape-the-outbox-pattern-message is updated with
@app.post('/state-outbox-transaction')
def outbox_transaction():
    from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType, DaprRequest

    transaction_operation = TransactionalStateOperation(
        operation_type=TransactionOperationType.upsert,
        key="1",
        data='{ "say": "hello world from outbox transaction" }',
    )

    print("/state-outbox-transaction: Execute the state transaction")
    c.execute_state_transaction(
        store_name=DAPR_STORE_NAME,
        operations=[transaction_operation],
        transactional_metadata={ 'datacontenttype': 'application/json'},
        metadata= (('datacontenttype', 'application/json'),)
    )
    
    print("/state-outbox-transaction: Transaction executed")
    return JSONResponse(content={"success": "true"})

Have to update the code here https://docs.dapr.io/developing-applications/building-blocks/state-management/howto-outbox/#override-dapr-generated-cloudevent-fields also

Feel free to do a docs contribution is you like

  1. I raised this issue in the Python SDK repo https://github.com/dapr/python-sdk/issues/790 and it would be amazing it you could raise a PR contribution for this.

msfussell avatar Mar 07 '25 05:03 msfussell

By configuring the state store, you can use the outbox pattern without any additional metadata in the requests. For example:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""
  - name: actorStateStore
    value: "true"
  - name: outboxPublishPubsub
    value: "pubsub". # This is the pubsub component name
  - name: outboxPublishTopic
    value: "test"

The additional metadata fields are needed to combine outbox and non-outbox messages on the same state store. Unfortunately, we currently don't support adding metadata for TransactionalStateOperation in the python-sdk, but I opened a PR here which will allow you to do something like:

from dapr.clients import DaprClient
from dapr.clients.grpc._request import TransactionalStateOperation, TransactionOperationType

DAPR_STORE_NAME = "statestore"

with DaprClient() as d:

    d.execute_state_transaction(store_name=DAPR_STORE_NAME, operations=[
        TransactionalStateOperation(key="key1", data="val1", metadata={"outbox.projection": "false"}),
        TransactionalStateOperation(key="key1", data="val2", metadata={"outbox.projection": "true"}),
    ],)

    print("Transaction with outbox pattern executed successfully!")

elena-kolevska avatar Mar 12 '25 13:03 elena-kolevska

@elena-kolevska the doc was updated with 4913 and this issue can be closed. TY!

bibryam avatar Oct 28 '25 09:10 bibryam