Skip to content

feat(pubsub/pulsar): register CloudEvents envelope Avro schema with Pulsar Schema Registry#4302

Merged
JoshVanL merged 1 commit into
dapr:mainfrom
javier-aliaga:pulsa-avro-cloudevents
Mar 26, 2026
Merged

feat(pubsub/pulsar): register CloudEvents envelope Avro schema with Pulsar Schema Registry#4302
JoshVanL merged 1 commit into
dapr:mainfrom
javier-aliaga:pulsa-avro-cloudevents

Conversation

@javier-aliaga

@javier-aliaga javier-aliaga commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Description

When Dapr publishes to Pulsar with CloudEvents wrapping enabled (the default), the wire format is a CloudEvents envelope containing the user's domain event in the data field. Previously, the Avro schema
registered with the Pulsar Schema Registry was the inner domain event schema, not the CloudEvents envelope — causing a mismatch between the schema in the registry and the actual messages stored in the topic.

This PR fixes the mismatch by wrapping the user-provided Avro schema inside a CloudEvents envelope Avro schema before registering it with the broker.

Default publish rawschema=true on publish request
No .avroschema Dapr wraps in CE envelope (JSON). No schema registered with Pulsar. Sent as raw bytes. Dapr sends raw bytes. No schema validation. No wrapping.
.avroschema without .rawschema flag Dapr wraps in CE envelope. Producer registers CE envelope schema. Validates against CE codec. Rejected with error. Producer schema is CE envelope, raw payload would mismatch.
.avroschema with .rawschema=true flag Misconfiguration. Producer registers inner schema but Dapr wraps in CE envelope — wire format won't match schema. Producer registers inner schema. Validates against inner codec. Wire format matches.

Changes

  • CloudEvents Avro schema wrapping (cloudevents_schema.go): new wrapInCloudEventsAvroSchema() embeds the user's inner schema as the data field of a CloudEvents envelope record, following the
    CloudEvents Avro format spec
  • Schema metadata (metadata.go): schemaMetadata now stores both the inner codec (codec/value) and the CE envelope codec (ceCodec/ceValue)
  • Producer & consumer registration (pulsar.go): use the CE envelope schema when available for both Publish and Subscribe
  • Avro publish validation (pulsar.go): select the correct codec (CE envelope vs inner) at publish time; reject rawschema=true on CE-wrapped topics with a clear error
  • Topic-level rawschema flag (pulsar.go, metadata.yaml): new <topic-name>.rawSchema=true metadata option skips CE envelope wrapping for dedicated raw-payload topics, registering the inner schema
    directly with the broker

Topic-level rawschema configuration

Pulsar enforces a single schema per topic. Since the CE envelope schema differs from the inner schema, rawsc hema=true messages cannot be sent to a CE-wrapped topic. This PR provides two options:

Default (CE-wrapped topic):

metadata:
  - name: orders.avroschema
    value: '{"type":"record","name":"Order","fields":[...]}'

Raw-payload-only topic (skips CE wrapping):
metadata:
  - name: orders-raw.avroschema
    value: '{"type":"record","name":"Order","fields":[...]}'
  - name: orders-raw.rawschema
    value: "true"

Publishing with rawschema=true to a CE-wrapped topic returns:
rawschema=true is not compatible with Avro schema topics using CloudEvents envelope; use a separate topic for raw payloads

Issue reference

We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.

Please reference the issue this PR will close: #[issue number]

Checklist

Please make sure you've completed the relevant tasks for this PR, out of the following list:

  • Code compiles correctly
  • Created/updated tests
  • Extended the documentation
    • Created the dapr/docs PR:

Note: We expect contributors to open a corresponding documentation PR in the dapr/docs repository. As the implementer, you are the best person to document your work! Implementation PRs will not be merged until the documentation PR is opened and ready for review.

@javier-aliaga javier-aliaga changed the title feat(pubsub/pulsar): register CloudEvents envelope Avro schema with P… feat(pubsub/pulsar): register CloudEvents envelope Avro schema with Pulsar Schema Registry Mar 20, 2026
@javier-aliaga
javier-aliaga requested a review from Copilot March 20, 2026 15:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

The PR implements registration of CloudEvents envelope Avro schemas with Pulsar Schema Registry. Previously, when Dapr published to Pulsar with CloudEvents wrapping enabled (the default), the schema registered with Pulsar was only the inner domain event schema, creating a mismatch with the actual wire format. This fix wraps the inner schema in a CloudEvents envelope Avro schema before registration.

Changes:

  • New CloudEvents schema wrapping functionality in cloudevents_schema.go that embeds inner Avro schemas as the data field of a CloudEvents envelope
  • Enhanced schemaMetadata structure to store both the inner schema codec and CloudEvents envelope codec
  • Updated producer and consumer registration to use the CloudEvents envelope schema when available
  • Added explicit error handling to reject rawPayload=true on Avro schema topics using CloudEvents envelopes
  • Comprehensive test coverage for the new functionality

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
pubsub/pulsar/cloudevents_schema.go New file implementing CloudEvents Avro schema wrapping logic
pubsub/pulsar/cloudevents_schema_test.go New comprehensive test file for schema wrapping with edge cases
pubsub/pulsar/metadata.go Enhanced schemaMetadata struct with CE envelope fields
pubsub/pulsar/pulsar.go Updated schema initialization, producer/consumer registration, and publish validation
pubsub/pulsar/pulsar_test.go Added test helpers and new test cases for CE envelope handling

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@javier-aliaga
javier-aliaga force-pushed the pulsa-avro-cloudevents branch from c3b6b2e to 28f9a05 Compare March 20, 2026 15:38
@javier-aliaga
javier-aliaga marked this pull request as ready for review March 20, 2026 15:45
@javier-aliaga
javier-aliaga requested review from a team as code owners March 20, 2026 15:45
Comment thread pubsub/pulsar/cloudevents_schema.go Outdated
Comment thread pubsub/pulsar/cloudevents_schema_test.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubsub/pulsar/pulsar.go
Comment thread pubsub/pulsar/cloudevents_schema.go Outdated
Comment thread pubsub/pulsar/pulsar.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubsub/pulsar/pulsar_test.go Outdated
Comment thread pubsub/pulsar/pulsar.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubsub/pulsar/pulsar.go Outdated
Comment thread pubsub/pulsar/pulsar_test.go Outdated
@javier-aliaga
javier-aliaga force-pushed the pulsa-avro-cloudevents branch 2 times, most recently from 6e2accb to f417293 Compare March 24, 2026 13:36
@javier-aliaga
javier-aliaga requested a review from Copilot March 24, 2026 13:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubsub/pulsar/metadata.yaml Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubsub/pulsar/pulsar_test.go Outdated
javier-aliaga added a commit to javier-aliaga/docs that referenced this pull request Mar 24, 2026
Document the new behavior where Dapr wraps user-provided Avro schemas
inside a CloudEvents envelope before registering with the Pulsar Schema
Registry. Add the new `<topic-name>.rawPayload` metadata field for
topics that need to skip CE wrapping and register the inner schema
directly.

Ref: dapr/components-contrib#4302
Signed-off-by: Javier Aliaga <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pubsub/pulsar/cloudevents_schema.go Outdated
Comment thread pubsub/pulsar/pulsar.go

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…ulsar Schema Registry

When Dapr publishes to Pulsar with CloudEvents wrapping (the default),
the actual wire format is a CloudEvents envelope containing the user's
domain event in the `data` field. Previously, the schema registered with
the Pulsar Schema Registry was the inner domain event schema, causing a
mismatch that breaks external consumers (e.g., Apache Flink) and may
cause Pulsar to reject messages when schema enforcement is enabled.

This change wraps the user-provided Avro schema inside a CloudEvents
envelope Avro schema before registering it with the broker. The envelope
includes all standard CE attributes (id, source, specversion, type),
optional attributes (datacontenttype, subject, time), Dapr extensions
(topic, pubsubname, traceid, traceparent, tracestate, expiration), and
the data/data_base64 fields. The inner domain schema is embedded as
the type of the nullable `data` union field.

Using rawPayload=true on a topic with an Avro schema now returns an
explicit error, since the broker enforces a single schema per topic and
mixing CE-wrapped and raw payloads is undefined behavior.

Signed-off-by: Javier Aliaga <[email protected]>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@JoshVanL
JoshVanL added this pull request to the merge queue Mar 26, 2026
Merged via the queue into dapr:main with commit 4ad2c5b Mar 26, 2026
108 checks passed
@dapr-bot

Copy link
Copy Markdown
Collaborator

The backport to release-1.16 failed:

The process '/usr/bin/git' failed with exit code 1

To backport manually, run these commands in your terminal:

# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add .worktrees/backport-release-1.16 release-1.16
# Navigate to the new working tree
cd .worktrees/backport-release-1.16
# Create a new branch
git switch --create backport-4302-to-release-1.16
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 4ad2c5bbe7873b9bb6044c2f1e736b47b55a145e
# Push it to GitHub
git push --set-upstream origin backport-4302-to-release-1.16
# Go back to the original working tree
cd ../..
# Delete the working tree
git worktree remove .worktrees/backport-release-1.16

Then, create a pull request where the base branch is release-1.16 and the compare/head branch is backport-4302-to-release-1.16.

@dapr-bot

Copy link
Copy Markdown
Collaborator

The backport to release-1.17 failed:

The process '/usr/bin/git' failed with exit code 1

To backport manually, run these commands in your terminal:

# Fetch latest updates from GitHub
git fetch
# Create a new working tree
git worktree add .worktrees/backport-release-1.17 release-1.17
# Navigate to the new working tree
cd .worktrees/backport-release-1.17
# Create a new branch
git switch --create backport-4302-to-release-1.17
# Cherry-pick the merged commit of this pull request and resolve the conflicts
git cherry-pick -x --mainline 1 4ad2c5bbe7873b9bb6044c2f1e736b47b55a145e
# Push it to GitHub
git push --set-upstream origin backport-4302-to-release-1.17
# Go back to the original working tree
cd ../..
# Delete the working tree
git worktree remove .worktrees/backport-release-1.17

Then, create a pull request where the base branch is release-1.17 and the compare/head branch is backport-4302-to-release-1.17.

javier-aliaga added a commit to javier-aliaga/components-contrib that referenced this pull request Mar 27, 2026
javier-aliaga added a commit to javier-aliaga/components-contrib that referenced this pull request Mar 27, 2026
javier-aliaga added a commit to javier-aliaga/components-contrib that referenced this pull request Mar 27, 2026
javier-aliaga added a commit to javier-aliaga/components-contrib that referenced this pull request Mar 27, 2026
javier-aliaga added a commit to javier-aliaga/docs that referenced this pull request Mar 30, 2026
Document the new behavior where Dapr wraps user-provided Avro schemas
inside a CloudEvents envelope before registering with the Pulsar Schema
Registry. Add the new `<topic-name>.rawPayload` metadata field for
topics that need to skip CE wrapping and register the inner schema
directly.

Ref: dapr/components-contrib#4302
Signed-off-by: Javier Aliaga <[email protected]>
javier-aliaga added a commit to javier-aliaga/docs that referenced this pull request Mar 30, 2026
Document the new behavior where Dapr wraps user-provided Avro schemas
inside a CloudEvents envelope before registering with the Pulsar Schema
Registry. Add the new `<topic-name>.rawPayload` metadata field for
topics that need to skip CE wrapping and register the inner schema
directly.

Ref: dapr/components-contrib#4302
Signed-off-by: Javier Aliaga <[email protected]>
javier-aliaga added a commit to javier-aliaga/docs that referenced this pull request Mar 30, 2026
Document the new behavior where Dapr wraps user-provided Avro schemas
inside a CloudEvents envelope before registering with the Pulsar Schema
Registry. Add the new `<topic-name>.rawPayload` metadata field for
topics that need to skip CE wrapping and register the inner schema
directly.

Ref: dapr/components-contrib#4302
Signed-off-by: Javier Aliaga <[email protected]>
javier-aliaga added a commit to javier-aliaga/docs that referenced this pull request Mar 30, 2026
Document the new behavior where Dapr wraps user-provided Avro schemas
inside a CloudEvents envelope before registering with the Pulsar Schema
Registry. Add the new `<topic-name>.rawPayload` metadata field for
topics that need to skip CE wrapping and register the inner schema
directly.

Ref: dapr/components-contrib#4302
Signed-off-by: Javier Aliaga <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants