Skip to content

feat(server): add Redis event publisher#1087

Closed
joonseolee wants to merge 1 commit intomiddleapi:mainfrom
joonseolee:feature/add-redis-event-publisher
Closed

feat(server): add Redis event publisher#1087
joonseolee wants to merge 1 commit intomiddleapi:mainfrom
joonseolee:feature/add-redis-event-publisher

Conversation

@joonseolee
Copy link
Copy Markdown
Contributor

@joonseolee joonseolee commented Oct 8, 2025

Summary

Adds RedisEventPublisher class to enable cross-server event handling using Redis Pub/Sub.

Changes

  • Add RedisEventPublisher class with publish/subscribe functionality
  • Support multiple Redis clients (ioredis, node-redis, upstash-redis)
  • Implement callback-based and async iterator subscriptions
  • Add comprehensive test coverage
  • Handle multiple subscribers for same event channel

Testing

  • Unit tests with mock Redis client

Fixes

#1011

Summary by CodeRabbit

  • New Features

    • Introduces a Redis-backed event system for cross-instance publish/subscribe.
    • Supports consumption via callbacks or async iterators, with configurable buffering and abort/cancellation.
    • Provides channel prefix configuration and graceful shutdown/cleanup.
    • Exposes listener count to track active subscriptions.
  • Tests

    • Adds comprehensive coverage for publishing, subscription (callbacks and async iteration), buffering limits, abort handling, unsubscribe behavior, and lifecycle cleanup.

@vercel
Copy link
Copy Markdown

vercel Bot commented Oct 8, 2025

@joonseolee is attempting to deploy a commit to the unnoq-team Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Oct 8, 2025
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @joonseolee, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This PR introduces a RedisEventPublisher for cross-server event handling via Redis Pub/Sub. It provides publish functionality and flexible subscribe methods (callback or async iterator), supporting various Redis clients and complex data serialization. The implementation includes robust testing and proper resource management.

Highlights

  • Intent: This pull request introduces a RedisEventPublisher class to enable cross-server event handling using Redis Pub/Sub. This allows for distributed event communication across multiple server instances, including serverless environments.
  • Changes: The core change is the addition of packages/server/src/helpers/redis-event-publisher.ts, which implements the RedisEventPublisher class. This class provides methods for publishing events to Redis channels and subscribing to them. It supports two subscription mechanisms: callback-based subscriptions and async iterator-based subscriptions (for for await...of loops, with optional buffering and abort signal support). The publisher uses a StandardRPCJsonSerializer to handle complex data types (like BigInt, Date, Set, Map) during serialization and deserialization. It also includes packages/server/src/helpers/redis-options.ts to define the RedisClient interface (compatible with ioredis, node-redis, upstash-redis) and RedisEventPublisherOptions. Comprehensive unit tests for the RedisEventPublisher are added in packages/server/src/helpers/redis-event-publisher.test.ts, covering publish, both subscription types, and resource cleanup. Finally, packages/server/src/helpers/index.ts is updated to export the new modules.
  • Reviewer Activity: No specific reviewer activity was provided in the context.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Oct 8, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new RedisEventPublisher helper with publish/subscribe APIs (callback and async iterator), associated types and defaults, plus tests. Updates helpers index to re-export the new module.

Changes

Cohort / File(s) Change Summary
Helpers index re-export
packages/server/src/helpers/index.ts
Adds export * from './redis-event-publisher' to expose the new helper.
Redis event publisher implementation
packages/server/src/helpers/redis-event-publisher.ts
Introduces RedisClient and RedisEventPublisherOptions interfaces, constants (DEFAULT_KEY_PREFIX, DEFAULT_MAX_BUFFERED_EVENTS), and RedisEventPublisher class with publish, subscribe (callback and async iterator), size, and close methods.
Redis event publisher tests
packages/server/src/helpers/redis-event-publisher.test.ts
Adds comprehensive tests covering publish formatting, callback subscriptions, async iterator behavior (buffering, abort, lifecycle), multiple subscribers, and cleanup via close using a mock Redis client.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Producer
  participant Pub as RedisEventPublisher
  participant Redis
  participant Sub as Subscriber (callback)

  rect rgb(245,248,255)
  note right of Pub: Channel = keyPrefix + event
  Producer->>Pub: publish(event, payload)
  Pub->>Pub: serialize(payload)
  Pub->>Redis: PUBLISH(channel, json)
  end

  par Subscribe (callback)
    Sub->>Pub: subscribe(event, listener)
    Pub->>Redis: SUBSCRIBE(channel)
    Redis-->>Pub: message(json)
    Pub->>Pub: deserialize(json)
    Pub-->>Sub: listener(payload)
  and Unsubscribe
    Sub->>Pub: unsubscribe()
    Pub->>Redis: UNSUBSCRIBE(channel)
  end
Loading
sequenceDiagram
  autonumber
  participant Consumer as Consumer (async iterator)
  participant Pub as RedisEventPublisher
  participant Redis

  Consumer->>Pub: subscribe(event, {signal?, maxBufferedEvents?})
  Pub->>Redis: SUBSCRIBE(channel)
  Redis-->>Pub: message(json)
  Pub->>Pub: deserialize -> enqueue buffer
  loop for-await-of
    Consumer->>Pub: next()
    Pub-->>Consumer: dequeued payload
  end
  alt Abort or return()
    Consumer->>Pub: return()/abort
    Pub->>Redis: UNSUBSCRIBE(channel)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • unnoq/orpc#1011 — Implements the Redis Event Publisher with publish/subscribe APIs and index export as described.

Poem

I hop through queues where messages play,
With whiskered ears on the Pub/Sub way.
Prefix the channel, nibble the byte,
Buffer some carrots for consumers at night.
When the work is done, I twitch and close—
Redis winds hush; off home I doze. 🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “feat(server): add Redis event publisher” succinctly captures the primary change of introducing a RedisEventPublisher to the server helpers, is clearly related to the implementation and tests provided, and follows a conventional commit style without extraneous details.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63ab1b0 and 19003b1.

📒 Files selected for processing (3)
  • packages/server/src/helpers/index.ts (1 hunks)
  • packages/server/src/helpers/redis-event-publisher.test.ts (1 hunks)
  • packages/server/src/helpers/redis-event-publisher.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/src/helpers/index.ts
🧰 Additional context used
🧬 Code graph analysis (2)
packages/server/src/helpers/redis-event-publisher.test.ts (1)
packages/server/src/helpers/redis-event-publisher.ts (2)
  • RedisClient (9-14)
  • RedisEventPublisher (75-281)
packages/server/src/helpers/redis-event-publisher.ts (3)
packages/client/src/adapters/standard/rpc-json-serializer.ts (2)
  • StandardRPCJsonSerializerOptions (25-27)
  • StandardRPCJsonSerializer (29-214)
packages/shared/src/json.ts (1)
  • stringifyJSON (9-12)
packages/server/src/helpers/redis-event-publisher.test.ts (1)
  • unsubscribe (25-27)
🔇 Additional comments (14)
packages/server/src/helpers/redis-event-publisher.test.ts (5)

6-37: LGTM!

The MockRedisClient implementation is well-designed for testing purposes. The use of setImmediate on line 15 correctly simulates the asynchronous nature of real Redis clients, ensuring tests validate actual async behavior.


58-93: LGTM!

The publish tests provide comprehensive coverage, including verification of channel naming, JSON structure, and serialization of complex types (BigInt, Date, Set, Map).


95-147: LGTM!

The callback subscription tests thoroughly validate subscription behavior, including message delivery to multiple subscribers and proper cleanup via the size getter.


149-168: LGTM!

The for-await-of test correctly validates async iterator behavior with proper cleanup via iterator.return().


170-190: LGTM!

The abort signal test properly validates that iterator cancellation works as expected.

packages/server/src/helpers/redis-event-publisher.ts (9)

9-14: LGTM!

The RedisClient interface provides a clean abstraction that supports multiple Redis client libraries (ioredis, node-redis, upstash-redis) as documented.


19-35: LGTM!

The options interface appropriately extends StandardRPCJsonSerializerOptions and provides sensible configuration for Redis channels and buffering.


40-45: LGTM!

The default values are reasonable: a namespaced key prefix prevents collisions, and a buffer of 100 events balances memory usage with tolerance for bursty traffic.


83-92: LGTM!

The constructor and size getter are correctly implemented. The size getter accurately counts all registered callbacks across all events.


98-104: LGTM!

The publish method correctly serializes payloads using only json and meta, intentionally excluding maps and blobs from the serialization. This is appropriate for event publishing via Redis Pub/Sub, which operates on strings rather than binary data.


120-142: LGTM!

The subscribe method overloads provide a clean API for both callback-based and async iterator-based subscription patterns, with the dispatcher correctly routing based on argument type.


144-187: LGTM!

The callback subscription implementation properly manages shared Redis channels with reference counting, ensuring the channel is unsubscribed only when the last listener is removed.


189-216: LGTM!

The iterator setup and listener logic are well-implemented. The buffering strategy correctly drops the oldest events when the buffer limit is exceeded, and the resolver pattern enables efficient pull-based consumption.


263-265: LGTM!

The channel naming is straightforward and correctly combines the prefix with the event name.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a well-structured RedisEventPublisher for handling events across servers. The implementation is clean and makes good use of modern TypeScript features. The addition of both callback and async iterator-based subscriptions provides a flexible API for consumers.

I've identified a few areas for improvement, primarily concerning asynchronous error handling and robustness, which are critical for a library component like this. I've also found an issue in one of the test cases.

My comments are focused on ensuring the implementation is as robust and type-safe as possible. Overall, this is a great addition to the project.

Comment on lines +207 to +208
expect(result1.value).toEqual({ message: 'First' })
expect(result2.value).toEqual({ message: 'Second' })
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.

high

There seems to be a logic error in this test case. The RedisEventPublisher is configured with maxBufferedEvents: 2. When three events are published ('First', 'Second', 'Third'), the buffer should contain the last two events due to the bufferedEvents.shift() logic when the buffer exceeds its max size. Therefore, the buffer should hold ['Second', 'Third'].

The test currently expects 'First' and 'Second', but it should expect 'Second' and 'Third' to be dequeued.

Suggested change
expect(result1.value).toEqual({ message: 'First' })
expect(result2.value).toEqual({ message: 'Second' })
expect(result1.value).toEqual({ message: 'Second' })
expect(result2.value).toEqual({ message: 'Third' })

Comment on lines +192 to +194
this.#subscribeWithCallback(event, listener).then((unsub) => {
unsubscribe = unsub
})
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.

high

The promise returned by this.#subscribeWithCallback is not handled. If the subscription fails (e.g., due to a Redis error), this will result in an unhandledRejection, which could crash the Node.js process. You should handle this potential error, for instance by using .catch().

When an error occurs, it should be propagated to the consumer of the async iterator by rejecting the pending promises in pullResolvers.

    this.#subscribeWithCallback(event, listener).then((unsub) => {
      unsubscribe = unsub
    }).catch((error) => {
      // Propagate subscription error to any waiting consumers
      pullResolvers.forEach(([, reject]) => reject(error))
      pullResolvers.length = 0
    })

Comment on lines +196 to +203
const abortListener = (event: any) => {
if (unsubscribe) {
unsubscribe()
}
pullResolvers.forEach(resolver => resolver[1](event.target.reason))
pullResolvers.length = 0
bufferedEvents.length = 0
}
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.

high

There are a few issues in this abortListener that could affect robustness:

  1. The unsubscribe() call returns a promise that is not handled. If it rejects, it will cause an unhandledRejection.
  2. The event parameter is typed as any. It's better to have a parameterless listener and use the signal object from the outer scope, which is more reliable.
  3. event.target.reason is used to get the abort reason. Using signal.reason is cleaner and safer. It's also good practice to provide a fallback error in case the reason is undefined.
    const abortListener = () => {
      if (unsubscribe) {
        unsubscribe().catch(err => console.error('Failed to unsubscribe on abort:', err))
      }
      const reason = signal?.reason ?? new Error('Aborted')
      pullResolvers.forEach(([, reject]) => reject(reason))
      pullResolvers.length = 0
      bufferedEvents.length = 0
    }

}
}
catch (error) {
console.error('Error processing Redis message:', error)
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.

medium

Using console.error for error handling might not be ideal for a library, as consumers may want to integrate with their own logging and monitoring systems. Consider providing a way for users to handle these errors, for example by emitting an 'error' event on the publisher or allowing an error handler to be passed in the constructor options. This would make the component more robust and configurable for production environments.

Comment on lines +239 to +241
for (const event of this.#subscribedChannels) {
await this.#redis.unsubscribe(this.#getChannelName(event))
}
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.

medium

The current for...of loop with await will stop if any of the unsubscribe calls fail. This could leave some channels subscribed and prevent the quit command from being called. To make the close method more robust, consider using Promise.allSettled to attempt unsubscribing from all channels, regardless of individual failures.

    const unsubscribePromises = Array.from(this.#subscribedChannels).map(event =>
      this.#redis.unsubscribe(this.#getChannelName(event)),
    )
    await Promise.allSettled(unsubscribePromises)

Comment on lines +35 to +37
serializerOptions?: {
customJsonSerializers?: readonly any[]
}
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.

medium

For better type safety and maintainability, it's recommended to use a more specific type for serializerOptions instead of an inline object type with any[].

You can import StandardRPCJsonSerializerOptions from @orpc/client/standard and use it here. This will ensure that the options passed to RedisEventPublisher match what StandardRPCJsonSerializer expects.

You'll need to add the following import at the top of the file:

import type { StandardRPCJsonSerializerOptions } from '@orpc/client/standard'
  serializerOptions?: StandardRPCJsonSerializerOptions

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/server/src/helpers/redis-event-publisher.test.ts (1)

194-211: Consider testing buffer overflow behavior.

The test verifies buffering up to maxBufferedEvents: 2 but doesn't explicitly validate what happens when a third event is published. Based on the implementation, the buffer should drop the oldest event when exceeding the limit.

Consider adding an assertion that explicitly tests the buffer overflow scenario:

 await publisher.publish('test-event', { message: 'First' })
 await publisher.publish('test-event', { message: 'Second' })
 await publisher.publish('test-event', { message: 'Third' })

 const iterator = subscription[Symbol.asyncIterator]()
 const result1 = await iterator.next()
 const result2 = await iterator.next()

 expect(result1.value).toEqual({ message: 'First' })
 expect(result2.value).toEqual({ message: 'Second' })
+
+// Publish a 4th event to test that 'First' was already consumed
+// and verify the buffer correctly held 'Second' and 'Third'
+await publisher.publish('test-event', { message: 'Fourth' })
+const result3 = await iterator.next()
+expect(result3.value).toEqual({ message: 'Third' })

Or alternatively, create a dedicated test that validates the buffer drops the oldest event when exceeding the limit by publishing 3 events before consuming any.

packages/server/src/helpers/redis-options.ts (1)

1-10: Document RedisClient adapter requirements
No built-in adapter exists for real clients—users must wrap libraries like ioredis (subscribe returns Promise<number>; uses events), node-redis v4 (callback signature), and upstash (REST API). Add a section (README or docs) outlining how to implement RedisClient for each, with example code.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca5123c and 63ab1b0.

📒 Files selected for processing (4)
  • packages/server/src/helpers/index.ts (1 hunks)
  • packages/server/src/helpers/redis-event-publisher.test.ts (1 hunks)
  • packages/server/src/helpers/redis-event-publisher.ts (1 hunks)
  • packages/server/src/helpers/redis-options.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/server/src/helpers/redis-event-publisher.test.ts (2)
packages/server/src/helpers/redis-options.ts (1)
  • RedisClient (5-10)
packages/server/src/helpers/redis-event-publisher.ts (1)
  • RedisEventPublisher (43-248)
packages/server/src/helpers/redis-event-publisher.ts (3)
packages/server/src/helpers/redis-options.ts (2)
  • RedisClient (5-10)
  • RedisEventPublisherOptions (15-38)
packages/client/src/adapters/standard/rpc-json-serializer.ts (1)
  • StandardRPCJsonSerializer (29-214)
packages/shared/src/json.ts (1)
  • stringifyJSON (9-12)
🔇 Additional comments (11)
packages/server/src/helpers/redis-event-publisher.test.ts (10)

6-37: LGTM! Well-designed mock for testing.

The MockRedisClient implementation correctly simulates Redis Pub/Sub behavior for testing purposes. The use of setImmediate to asynchronously deliver published messages to subscribers accurately mimics real Redis behavior and ensures proper testing of async event handling.


46-52: LGTM! Proper test setup with custom prefix.

The test setup correctly initializes a fresh MockRedisClient and RedisEventPublisher instance for each test with a custom keyPrefix. This ensures test isolation and validates the channel naming functionality.


58-73: LGTM! Validates publish and serialization format.

The test correctly verifies that:

  • Events are published to the expected Redis channel (test:test-event)
  • The serialization format follows the [json, meta, maps, blobs] structure
  • Simple payloads serialize correctly with empty metadata arrays

75-94: LGTM! Comprehensive complex type serialization test.

The test validates that the serializer correctly handles:

  • BigInt → string representation
  • Date → ISO 8601 string
  • Set → array
  • Map → array of tuples

This ensures cross-server compatibility when publishing complex JavaScript types.


98-114: LGTM! Correct handling of async message delivery.

The test properly accounts for async message delivery by:

  1. Awaiting subscription to ensure setup is complete
  2. Publishing the event
  3. Using setImmediate callback to wait for async delivery
  4. Cleaning up with unsubscribe

116-137: LGTM! Validates multiple subscriber fan-out.

The test correctly verifies that multiple subscribers on the same event channel all receive the published message, which is essential for distributed event handling across server instances.


139-148: LGTM! Validates cleanup and listener count tracking.

The test confirms that:

  • The size property accurately tracks active listeners
  • Unsubscribing properly cleans up resources
  • The listener count returns to zero after unsubscribe

152-170: LGTM! Validates async iterator consumption.

The test correctly demonstrates manual async iterator usage with Symbol.asyncIterator and validates that:

  • Buffered events are delivered in FIFO order
  • The iterator protocol (next(), return()) works correctly

172-192: LGTM! Validates abort signal integration.

The test correctly verifies that AbortSignal integration works by:

  1. Creating a subscription with an AbortController
  2. Aborting after a delay
  3. Confirming that pending iterator.next() calls reject with AbortError

This ensures proper cleanup when operations are cancelled.


214-224: LGTM! Validates resource cleanup on close.

The test confirms that calling close() properly:

  • Cleans up all subscriptions
  • Resets the listener count to zero
  • Closes the Redis connection
packages/server/src/helpers/redis-options.ts (1)

12-38: LGTM! Well-documented configuration options.

The RedisEventPublisherOptions interface is well-structured with:

  • Clear documentation for each option
  • Sensible defaults mentioned in JSDoc
  • Flexible serialization configuration

The serializerOptions.customJsonSerializers uses readonly any[] for flexibility, which is acceptable given the serialization framework's requirements.

@codecov
Copy link
Copy Markdown

codecov Bot commented Oct 8, 2025

Codecov Report

❌ Patch coverage is 91.36691% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ckages/server/src/helpers/redis-event-publisher.ts 91.30% 12 Missing ⚠️

📢 Thoughts on this report? Let us know!

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Oct 8, 2025

More templates

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@1087

@orpc/client

npm i https://pkg.pr.new/@orpc/client@1087

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@1087

@orpc/experimental-durable-iterator

npm i https://pkg.pr.new/@orpc/experimental-durable-iterator@1087

@orpc/hey-api

npm i https://pkg.pr.new/@orpc/hey-api@1087

@orpc/interop

npm i https://pkg.pr.new/@orpc/interop@1087

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@1087

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@1087

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@1087

@orpc/openapi-client

npm i https://pkg.pr.new/@orpc/openapi-client@1087

@orpc/otel

npm i https://pkg.pr.new/@orpc/otel@1087

@orpc/react

npm i https://pkg.pr.new/@orpc/react@1087

@orpc/react-query

npm i https://pkg.pr.new/@orpc/react-query@1087

@orpc/experimental-react-swr

npm i https://pkg.pr.new/@orpc/experimental-react-swr@1087

@orpc/server

npm i https://pkg.pr.new/@orpc/server@1087

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@1087

@orpc/solid-query

npm i https://pkg.pr.new/@orpc/solid-query@1087

@orpc/standard-server

npm i https://pkg.pr.new/@orpc/standard-server@1087

@orpc/standard-server-aws-lambda

npm i https://pkg.pr.new/@orpc/standard-server-aws-lambda@1087

@orpc/standard-server-fetch

npm i https://pkg.pr.new/@orpc/standard-server-fetch@1087

@orpc/standard-server-node

npm i https://pkg.pr.new/@orpc/standard-server-node@1087

@orpc/standard-server-peer

npm i https://pkg.pr.new/@orpc/standard-server-peer@1087

@orpc/svelte-query

npm i https://pkg.pr.new/@orpc/svelte-query@1087

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@1087

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@1087

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@1087

@orpc/vue-colada

npm i https://pkg.pr.new/@orpc/vue-colada@1087

@orpc/vue-query

npm i https://pkg.pr.new/@orpc/vue-query@1087

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@1087

commit: 19003b1

@@ -0,0 +1,38 @@
/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we need another file for there stuffs (almost related to publisher class), so I think think should merge back into redis-event-publisher.ts

/**
* Options for Redis Event Publisher
*/
export interface RedisEventPublisherOptions {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This interface should extends StandardRPCJsonSerializerOptions instead of require user provide it through serializerOptions

Comment thread packages/server/src/helpers/redis-event-publisher.ts
}
}
catch (error) {
console.error('Error processing Redis message:', error)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should rethrow error here avoid console as much as possible

Comment thread packages/server/src/helpers/redis-event-publisher.ts
@joonseolee joonseolee force-pushed the feature/add-redis-event-publisher branch from 63ab1b0 to 19003b1 Compare October 10, 2025 09:34
@dinwwwh
Copy link
Copy Markdown
Member

dinwwwh commented Oct 12, 2025

Thanks @joonseolee! It's looking pretty good now (just a few small bugs left). I'm planning to introduce @orpc/publisher, a unified interface for Redis, in-memory, and other adapters - making it easy to swap between them. I'll build on your Redis Pub/Sub adapter implementation and add you as a co-author.

dinwwwh added a commit that referenced this pull request Oct 19, 2025
- [x] memory
- [x] ioredis
- [x] upstash redis
- [x] docs
- [x] random prefix all keys when tests to avoid conflict
- [x] handle error after subscribe success

Closes: #1011 #1087

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* New Publisher system: buffered publish/subscribe with async-iterator
support and adapters for in-memory, IORedis and Upstash Redis including
optional resume/replay.

* **Documentation**
* Publisher guide, helper docs, and event-iterator examples added;
package README updated.

* **Tests**
* Extensive unit and integration suites (including real Redis/Upstash
scenarios); CI configured to run Redis during tests.

* **Chores**
* .env.example, .gitignore, package configs, TS project config and test
runner updates; ID sequencing/compare behavior adjusted.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Joonseo Lee <niceweather94@gmail.com>
@dinwwwh
Copy link
Copy Markdown
Member

dinwwwh commented Oct 19, 2025

@orpc/experimental-publisher now available, thanks @joonseolee

@dinwwwh dinwwwh closed this Oct 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants