GithubHelp home page GithubHelp logo

qweeze / rstream Goto Github PK

View Code? Open in Web Editor NEW
68.0 6.0 11.0 379 KB

A Python asyncio-based client for RabbitMQ Streams

License: MIT License

Python 100.00%
python rabbitmq rabbitmq-streams asyncio rabbitmq-client

rstream's Introduction

RabbitMQ Stream Python Client

A Python asyncio-based client for RabbitMQ Streams

The RabbitMQ stream plug-in is required. See the documentation for enabling it.

Table of Contents

Installation

The RabbitMQ stream plug-in is required. See the documentation for enabling it.

The client is distributed via PIP:

 pip install rstream

Examples

Here you can find different examples.

Client Codecs

Before start using the client is important to read this section. The client supports two codecs to store the messages to the server:

  • AMQP 1.0
  • Binary

By default you should use AMQP 1.0 codec:

   amqp_message = AMQPMessage(
    body=bytes("hello: {}".format(i), "utf-8"),
  )

AMQP 1.0 codec vs Binary

You need to use the AMQP 1.0 codec to exchange messages with other stream clients like Java, .NET, Rust, Go or if you want to use the AMQP 0.9.1 clients.

You can use the Binary version if you need to exchange messages from Python to Python.

Note: The messages stored in Binary are not compatible with the other clients and with AMQP 0.9.1 clients.
Once the messages are stored to the server, you can't change them.

Read also the Client Performances section

Publishing messages

You can publish messages with four different methods:

  • send: asynchronous, messages are automatically buffered internally and sent at once after a timeout expires.
  • send_batch: synchronous, the user buffers the messages and sends them. This is the fastest publishing method.
  • send_wait: synchronous, the caller wait till the message is confirmed. This is the slowest publishing method.
  • send_sub_entry: asynchronous. See Sub-entry batching and compression.

On the examples directory you can find diffent way to send the messages:

Publishing with confirmation

The Send method takes as parameter an handle function that will be called asynchronously when the message sent will be notified from the server to have been published.

Example:

With send_wait instead will wait until the confirmation from the server is received.

Sub-Entry Batching and Compression

RabbitMQ Stream provides a special mode to publish, store, and dispatch messages: sub-entry batching. This mode increases throughput at the cost of increased latency and potential duplicated messages even when deduplication is enabled. It also allows using compression to reduce bandwidth and storage if messages are reasonably similar, at the cost of increasing CPU usage on the client side.

Sub-entry batching consists in squeezing several messages – a batch – in the slot that is usually used for one message. This means outbound messages are not only batched in publishing frames, but in sub-entries as well.

  # sending with compression
   await producer.send_sub_entry(
        STREAM, compression_type=CompressionType.Gzip, sub_entry_messages=messages
   )

Full example producer using sub-entry batch

Consumer side is automatic, so no need configurations.

The client is shipped with No Compression (CompressionType.No) and Gzip Compression (CompressionType.Gzip) the other compressions (Snappy, Lz4, Zstd) can be used implementing the ICompressionCodec class.

Deduplication

RabbitMQ Stream can detect and filter out duplicated messages, based on 2 client-side elements: the producer name and the message publishing ID. All the producer methods to send messages (send, send_batch, send_wait) takes a publisher_name parameter while the message publishing id can be set in the AMQP message.

Example:

Consuming messages

See consumer examples for basic consumer and consumers with different offsets.

Server-side offset tracking

RabbitMQ Streams provides server-side offset tracking for consumers. This features allows a consuming application to restart consuming where it left off in a previous run. You can use the store_offset (to store an offset in the server) and query_offset (to query it) methods of the consumer class like in this example:

Superstreams

A super stream is a logical stream made of individual, regular streams. It is a way to scale out publishing and consuming with RabbitMQ Streams: a large logical stream is divided into partition streams, splitting up the storage and the traffic on several cluster nodes.

See the blog post for more info.

You can use superstream_producer and superstream_consumer classes which internally uses producers and consumers to operate on the componsing streams.

See the Super Stream example

Single Active Consumer

Single active consumer provides exclusive consumption and consumption continuity on a stream.
See the blog post for more info. See examples in:

See the single active consumer example

Filtering

Filtering is a new streaming feature enabled from RabbitMQ 3.13 based on Bloom filter. RabbitMQ Stream provides a server-side filtering feature that avoids reading all the messages of a stream and filtering only on the client side. This helps to save network bandwidth when a consuming application needs only a subset of messages.

https://rabbitmq.github.io/rabbitmq-stream-java-client/stable/htmlsingle/#filtering

See the filtering examples

Connecting with SSL

You can enable ssl/tls. See example here: tls example

Sasl Mechanisms

You can use the following sasl mechanisms:

  • PLAIN
  • EXTERNAL

The client uses PLAIN mechanism by default.

The EXTERNAL mechanism is used to authenticate a user based on a certificate presented by the client. Example:

    ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    # put the root certificate of the ca
    ssl_context.load_verify_locations("certs/ca_certificate.pem")
    ssl_context.load_cert_chain(
        "certs/client_HOSTNAME_certificate.pem",
        "certs/client_HOSTNAME_key.pem",
    )

    async with Producer(
        "HOSTNAME",
        username="not_important",
        password="not_important",
        port=5551,
        ssl_context=ssl_context,
        sasl_configuration_mechanism=SlasMechanism.MechanismExternal ## <--- here EXTERNAL configuration

The plugin rabbitmq_auth_mechanism_ssl needs to be enabled on the server side, and ssl_options.fail_if_no_peer_cert needs to set to true config example:

auth_mechanisms.3 = PLAIN
auth_mechanisms.2 = AMQPLAIN
auth_mechanisms.1 = EXTERNAL

ssl_options.cacertfile = certs/ca_certificate.pem
ssl_options.certfile = certs/server_certificate.pem
ssl_options.keyfile = certs/server_key.pem
listeners.ssl.default = 5671
stream.listeners.ssl.default = 5551
ssl_options.verify               = verify_peer
ssl_options.fail_if_no_peer_cert = true

Managing disconnections

The client supports auto-reconnect just for Producer and SuperstreamProducer at the moment.

When the TCP connection is disconnected unexpectedly, the Producer and the SuperstreamProducer will try to automatically reconnect while in case of the Consumer/SuperstreamConsumer the client raises an event that needs to be managed:

async def on_connection_closed(disconnection_info: DisconnectionErrorInfo) -> None:
    print(
        "connection has been closed from stream: "
        + str(disconnection_info.streams)
        + " for reason: "
        + str(disconnection_info.reason)
    )

consumer = Consumer(
..        
on_close_handler=on_connection_closed,
)

Reconnect

When the on_close_handler event is raised, you can close the Consumers by doing a correct clean-up or try reconnecting using the reconnect stream.

Example:

 async def on_connection_closed(disconnection_info: OnClosedErrorInfo) -> None:
        print(
            "connection has been closed from stream: "
            + str(disconnection_info.streams)
            + " for reason: "
            + str(disconnection_info.reason)
        )

        for stream in disconnection_info.streams:
            print("reconnecting stream: " + stream)
            await producer.reconnect_stream(stream)

Please take a look at the complete reliable client example here

Metadata Update

If the streams topology changes (ex:Stream deleted or add/remove follower), The server removes the producers and consumers linked to the stream and then it sends the Metadata update event. the behaviour is similar to what we have for disconnections. In case of the Producer/Superstream Producer the Client will try to automatically reconnect while the Consumer needs to manage the on_close_handler event.

Please take a look at the complete reliable client example here

Load Balancer

In order to handle load balancers, you can use the load_balancer_mode parameter for producers and consumers. This will always attempt to create a connection via the load balancer, discarding connections that are inappropriate for the client type.

Producers must connect to the leader node, while consumers can connect to any, prioritizing replicas if available.

Client Performances

The RabbitMQ Stream queues can handle high throughput. Currently, the client cannot reach the maximum throughput the server can handle.

We found some bottlenecks; one of them is the current AMQP 1.0 marshal and unmarshal message format.

This one:

 for i in range(1_000_000):
            amqp_message = AMQPMessage(
                body=bytes("hello: {}".format(i), "utf-8"),
            )
            # send is asynchronous
            await producer.send(stream=STREAM, message=amqp_message)

is more or less ~55% slower than:

 for i in range(1_000_000):
            # send is asynchronous
            await producer.send(stream=STREAM, message=b"hello")

You can use the batch_send to test the performances.

We are evaluating rewriting the AMQP 1.0 codec optimized for the stream use case.

Test case

  • Linux Ubuntu 4 cores and 8 GB of Ram

  • RabbitMQ installed to the server

  • Send batch with AMQP 1.0 codec:

$  python3 docs/examples/basic_producers/producer_send_batch.py
Sent 1.000.000 messages in 9.3218 seconds. 107.275,5970 messages per second
  • Send batch with binary codec:
$ python3 docs/examples/basic_producers/producer_send_batch_binary.py
Sent 1.000.000 messages in 2.9930 seconds. 334.116,5639 messages per second

Build and Test

To run the tests, you need to have a running RabbitMQ Stream server. You can use the docker official image.

Run the server with the following command:

docker run -it --rm --name rabbitmq -p 5552:5552 -p 5672:5672 -p 15672:15672 \
    -e RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS='-rabbitmq_stream advertised_host localhost' \
    rabbitmq:3.12-management

enable the plugin:

docker exec rabbitmq rabbitmq-plugins enable rabbitmq_stream

and run the tests:

 poetry run pytest

Project Notes

The project is in development and stabilization phase. Features and API are subject to change, but breaking changes will be kept to a minimum.
Any feedback or contribution is welcome

rstream's People

Contributors

danielepalaia avatar gsantomaggio avatar qweeze avatar wonesy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

rstream's Issues

Improve consumer callback to provide more informations

At the moment the consumer callback is just returning the message:

    def on_message(msg: AMQPMessage):
        print('Got message: {}'.format(msg.body))

    await consumer.start()
    await consumer.subscribe('mystream', on_message, decoder=amqp_decoder)
    await consumer.run()

Withe the superstream implementation we may need further info to be returned like the name of the stream, the offset and the timestamp of the message

Implement single active-consumer

This issue to track the development of single-active consumer.

Superstream was implemented here: #58

We decide to implement single-active consumer on a separate PR

Refactor _send_batch function

From this implementation 6f2878e
we implemented Send and SendBatch to take in input a notification callback, in a way that every Send or SendBatch can have a dedicated notification callback to be registered.

There is a bug in _send_batch that prevent this to happen as apparently just the last notification callback registered get invoked for all the calls.

As _send_batch has been recently extended to support also sub_entry_batching messages, maybe to simplify _send_batch we may think just to support one callback function maybe created at Producer level.

test_offset_type_timestamp test failing

It seems like this unit test test_offset_type_timestamp is failing from time to time so in my previous submission I had to comment it out.

            except exceptions.CancelledError as exc:
               raise exceptions.TimeoutError() from exc
               asyncio.exceptions.TimeoutError

/usr/local/Cellar/[email protected]/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/tasks.py:458: TimeoutError

To investigate why this is happening

Add documentation or scripts for test setup

I dug through the fixtures and CI to reverse engineer how it was run, but it would be useful to add a script to start all of this up, or some docker compose files.

README could be augmented with a quick testing section

Add more corner case unit tests

Can be done together with #67

To check carefully (and unit test) corner case conditions and exceptions managements like (stream not present, callbacks passed to functions not present or not correct ecc ecc...)

PyPi Source is not available !!

PyPi Source is not available with pip3 install rstream
https://pypi.org/project/rstream/

Error message

user@user-virtual-machine1:~$ pip3 install rstream
ERROR: Could not find a version that satisfies the requirement rstream (from versions: none)
ERROR: No matching distribution found for rstream

pip3 version : 20.0.2
OS : Ubuntu 20.04.3

Stream already exists error

I am using the publisher example from the readme file.

First run

Traceback (most recent call last):
  File "/home/wrobell/projects/snippets/rabbitmq/rabbitmq.py", line 11, in <module>
    asyncio.run(publish())
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/wrobell/projects/snippets/rabbitmq/rabbitmq.py", line 9, in publish
    await producer.publish('mystream', f'msg: {i}'.encode())
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/producer.py", line 194, in publish
    publishing_ids = await self.publish_batch(
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/producer.py", line 156, in publish_batch
    publisher = await self._get_or_create_publisher(stream, publisher_name)
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/producer.py", line 110, in _get_or_create_publisher
    client = await self._get_or_create_client(stream)
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/producer.py", line 94, in _get_or_create_client
    self._clients[stream] = await self._pool.get((leader.host, leader.port))
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 481, in get
    self._clients[addr] = await self.new(addr)
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 495, in new
    await client.authenticate(
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 265, in authenticate
    await self.sync_request(
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 127, in sync_request
    resp.check_response_code()
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/schema.py", line 35, in check_response_code
    raise ServerError.from_code(code)
rstream.exceptions.SASLAuthenticationFailureLoopback

Please note that above error happens, when publishing a message to the stream. The related queue got created:

# rabbitmqctl list_queues
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
name    messages
mystream        0

I can publish a message to the queue using Pika. Not sure, if it is configuration issue on my side...

Anyway, trying again to run the same publisher example

Traceback (most recent call last):
  File "/home/wrobell/projects/snippets/rabbitmq/rabbitmq.py", line 11, in <module>
    asyncio.run(publish())
  File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "/home/wrobell/projects/snippets/rabbitmq/rabbitmq.py", line 6, in publish
    await producer.create_stream('mystream')
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/producer.py", line 228, in create_stream
    await self.default_client.create_stream(stream, arguments)
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 292, in create_stream
    await self.sync_request(
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 127, in sync_request
    resp.check_response_code()
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/schema.py", line 35, in check_response_code
    raise ServerError.from_code(code)
rstream.exceptions.StreamAlreadyExists

I would expect the stream to be reused when calling Producer.create_stream.

bug in superstream_consumer

After a while consumer fails:

Traceback (most recent call last):
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/python_rstream/consumer.py", line 35, in <module>
    asyncio.run(consume())
  File "/usr/local/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/Cellar/[email protected]/3.11.3/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/python_rstream/consumer.py", line 31, in consume
    await consumer.subscribe(callback=on_message, decoder=amqp_decoder, offset_specification=offset_specification)
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/superstream_consumer.py", line 143, in subscribe
    consumer = await self._create_consumer()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/superstream_consumer.py", line 176, in _create_consumer
    await consumer.start()
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/consumer.py", line 110, in start
    self._default_client = await self._pool.get(connection_closed_handler=self._connection_closed_handler)
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/client.py", line 566, in get
    self._clients[desired_addr] = await self.new(
                                  ^^^^^^^^^^^^^^^
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/client.py", line 610, in new
    await client.start()
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/client.py", line 170, in start
    await self._conn.open()
  File "/Users/dpalaia/projects/rabbitmq-stream-mixing/python/venv/lib/python3.11/site-packages/rstream/connection.py", line 42, in open
    raise ConnectionError(f"Could not connect to {self.host}:{self.port}")
ConnectionError: Could not connect to localhost:5552
make: *** [consumer] Error 1

Change the On_message function

See #61 (comment)

Per conversation with @DanielePalaia.
It would be helpful to extend the consumer message handler like the other clients:

 MessageHandler = async (stream, consumer, context, message) => 
            {
           }

stream = the stream name where the message comes from
consumer = the consumer instance. It can be used to store the offset etc
context = it should contain info like: Offset and chunk timestamp

We open a separate issue

specify a timeout option to send_wait

At the moment send_wait waits until publish_confirmation is received from the server.

Better to put a timeout that will allow the function to continue if the timeout expires even if the message is not yet confirmed

send_sub_entry_batch too slow

Maybe related to #84

But in general it seems very slow, very slower comparing to send() too.

To check also it if is related to compression or how batching are created

Implement examples

Provide a doc folder where we can put working python examples using the client for the main functionalities: Send, SendBatch, SendSubEntry, Subscribe, Superstream implementation, single active consumer ecc...

Making publish asynchronous using a batching mechanism internally

At the moment all of our RabbitMQ stream clients are using the publish asynchronously using a buffering mechanism internally to send messages after a certain amount of time.

Like in GO/Java/Rust clients:
https://github.com/rabbitmq/rabbitmq-stream-go-client
https://rabbitmq.github.io/rabbitmq-stream-java-client/stable/htmlsingle/

For example the GO client is using a Ticker in order to batch messages for a while and then send them with SendBatch when the Ticker timeout.

Probably we can do a similar implementation on this client too.

Performance issue in current superstream implementation

At the moment everytime we send a new message to a superstream, ROUTE and PARTITIONS commands are always called causing unnecessary requests to the server and degrading the performance. We should call these once and put them in a cache for the SuperStreamProducer to use.

test rstream against python 3.11

Currently we are using python 3.9.

It seems like there can be few issues with Python 3.11.

Scope of this task is to explore / see potential issues and try to correct them.

Also to think on a possible cross-testing Python3.9 and 3.11

Support for load balancers within docker - official workaround

Referencing this article: https://blog.rabbitmq.com/posts/2021/07/connecting-to-streams/

There's a section on load balancers which states:

Client Workaround With a Load Balancer
A client application can still always connect to the load balancer and end up connected to the appropriate node. How can it do it? Two steps:

- use the metadata command but ignore the information and always connect to the load balancer
- retry connecting until it gets connected to the appropriate node

You may wonder how to find out about the node once a connection is established? The “coordinates” of the node (hostname and port, or advertised_host and advertised_port if configured) are available in a stream protocol connection. So a client application can know to which node it is connected to.

The default codepath for publish, for example, queries the leader and then attempts to connect directly. This doesn't work in a load balancer environment.

I'm not sure what the appropriate way to handle this is, either a parameter in the Producer class or a new function publish_to_load_balancer(...)

To review deduplication

This issue just to carefully review/test how deduplication is working.

To see how publisher_name /message_id are manage and if everything's working fine with it.

There are actually some tests using RawMessage using different/same message_id.
To see with different publisher_name as well

Handle MetadataUpdate

MetadataUpdate is raised when there is a change in the stream topology.
As the first implementation, we can have an event, so the user can be aware of that.

Cannot read messages using the examples from readme file

To replicate run the producer from the readme file to create stream and publish messages

$ rabbitmqctl list_queues
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
name    messages
mystream        100

Then run consumer from the readme file. I am getting a lot of errors

Error while running handler functools.partial(<bound method Consumer._on_deliver of <rstream.consumer.Consumer object at 0x7fc27868c250>>, subscriber=_Subscriber(stream='mystream', subscription_id=1, reference='mystream_subscriber_1', client=<rstream.client.Client object at 0x7fc278697a00>, callback=<function consume.<locals>.on_message at 0x7fc277cb3e50>, decoder=<function amqp_decoder at 0x7fc2780b71f0>, offset_type=<OffsetType.FIRST: 1>, offset=0)) of frame Deliver(subscription_id=1, magic_version=80, chunk_type=0, num_entries=1, num_records=1, timestamp=1634293745617, epoch=1, chunk_first_offset=99, chunk_crc=3688700533, data_length=11, trailer_length=30, _reserved=0, data=b'\x00\x00\x00\x07msg: 99')
Traceback (most recent call last):
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/client.py", line 157, in _listener
    await maybe_coro
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/consumer.py", line 228, in _on_deliver
    maybe_coro = subscriber.callback(subscriber.decoder(message))
  File "/home/wrobell/.local/lib/python3.9/site-packages/rstream/amqp.py", line 26, in amqp_decoder
    message.decode(data)
  File "/home/wrobell/.local/lib/python3.9/site-packages/proton/_message.py", line 499, in decode
    self._check(pn_message_decode(self._msg, data))
  File "/home/wrobell/.local/lib/python3.9/site-packages/proton/_message.py", line 80, in _check
    raise exc("[%s]: %s" % (err, pn_error_text(pn_message_error(self._msg))))
proton._exceptions.MessageException: [-6]: data error: (null)

I am using RabbitMQ 3.9.7, rstream 0.3.0.

Implements server side offset storing

At the moment the offset is stored automatically in the server in the consumer side.

We should allow the user of the client to be able to commit the offset whenever needed and allow to use the query_offset and store_offset function from the caller

publish raise socket.gaierror: [Errno -2] Name or service not known

ubuntu 20.04 + python 3.10 + rabbitMQ 3.9.13 from docker image
dockerfile:

FROM rabbitmq:3-management
RUN rabbitmq-plugins enable rabbitmq_web_stomp rabbitmq_stream

docker-compose.yml

version: '3.8'

services:
  rabbitmq-webstomp:
    restart: always
    image: rabbitmq-webstomp
    build: ./
    command: rabbitmq-server
    environment:
      RABBITMQ_ERLANG_COOKIE: SWQOKODSQALRPCLNMEQG
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    ports:
      - "15672:15672" 
      - "5672:5672" 
      - "15674:15674" 
      - "61613:61613" 
      - "5552:5552" 

run readme demo:

import asyncio
from rstream import Producer, AMQPMessage

async def publish():
    async with Producer('localhost', username='guest', password='guest') as producer:
        await producer.create_stream('mystream', exists_ok=True)

        for i in range(100):
            amqp_message = AMQPMessage(
                body='hello: {}'.format(i),
            )
            #msg = f'hello: {i}'
            await producer.publish('mystream', amqp_message)

asyncio.run(publish())

the queue "mystream" is created , http://127.0.0.1:15672/#/queues/%2F/mystream,
but "await producer.publish('mystream', amqp_message)" failed

Traceback (most recent call last):
File "/home/xuqinghan/dev/test-cljs-rabbitmq/test/test_rstream.py", line 19, in
asyncio.run(publish())
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
return future.result()
File "/home/xuqinghan/dev/test-cljs-rabbitmq/test/test_rstream.py", line 17, in publish
await producer.publish('mystream', amqp_message)
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/producer.py", line 196, in publish
publishing_ids = await self.publish_batch(
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/producer.py", line 158, in publish_batch
publisher = await self._get_or_create_publisher(stream, publisher_name)
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/producer.py", line 112, in _get_or_create_publisher
client = await self._get_or_create_client(stream)
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/producer.py", line 96, in _get_or_create_client
self._clients[stream] = await self._pool.get((leader.host, leader.port))
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/client.py", line 468, in get
self._clients[addr] = await self.new(addr)
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/client.py", line 482, in new
await client.start()
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/client.py", line 136, in start
await self._conn.open()
File "/home/xuqinghan/.local/lib/python3.10/site-packages/rstream/connection.py", line 33, in open
self._reader, self._writer = await asyncio.wait_for(
File "/usr/local/lib/python3.10/asyncio/tasks.py", line 445, in wait_for
return fut.result()
File "/usr/local/lib/python3.10/asyncio/streams.py", line 47, in open_connection
transport, _ = await loop.create_connection(
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 1016, in create_connection
infos = await self._ensure_resolved(
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 1395, in _ensure_resolved
return await loop.getaddrinfo(host, port, family=family, type=type,
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 855, in getaddrinfo
return await self.run_in_executor(
File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/usr/local/lib/python3.10/socket.py", line 955, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known

Making publish confirmation asynchronous

It is my understanding that at the moment we can use the sync flag of the publish/publish_batch to block the API and wait for confirmation events to arrive.

We may need to manage it at client side asynchronously like for example we are doing with the GO client:

https://github.com/rabbitmq/rabbitmq-stream-go-client#publish-confirmation

Like the client set up an handle function that wait for confirmations to arrive.
Channels are not available with python asyncio but we can use futures or another sync mechanism.

Performance issue on send() due to asyncio.sleep(0)

At the moment every time we call send() we are using an asyncio.sleep(0) in order to force a context switch and give the background thread which is sending the batched messages time to do its work.

This is due on how the asyncio library is working and it was already discussed here:
#32

See references here:
https://towardsdatascience.com/asyncio-is-not-parallelism-70bfed470489
and
https://superfastpython.com/asyncio-sleep/)

But we discovered that calling it every time on every send() is impacting performance.
We should think on a way to limit the amount of times we call it (once after 1000 messages batched should be enough for example, or allow a configuration parameter to decide after how many messages the user wants to do it)

This should be also well documented (on the code and on the documentation)

Performances

Test the performance and evaluate a way to aggregate the messages before sending the messages.

TLS Support

It would be useful to add TLS Support.
TLS port 5551

Replace _buffered_messages with an asyncio.Queue

To see the possibility to replace this shared structure (dictionary of list) in send() with the relative mutex with an asyncio.Queue.
I made a few tests and it seems like using asyncio.Queue decrease performance, but maybe we need to spend more time for this investigation.

Another possibile would be to improve the locking mechanism (At the moment just a lock protect the entire structure)

Next steps

Hi @qweeze
Here is the plan for the following features we would like to implement:

Add onClose call back when the client is disconnected from the server

It would useful to have a notification when the client is disconnected from the server for some reason.
Especially if you want to implement some auto-reconnect feature

For example:

new ConsumerConfig / ProducerConfig
    {
..       
 ConnectionClosedHandler = async s =>
        {
            Console.WriteLine("client disconnected");
            await Task.CompletedTask;
        },
...

stompjs client cant subscribe msg published by rstream?

Publishing messages and Consuming messages is ok. https://github.com/qweeze/rstream/blob/master/README.md
but when use stormjs to consume "mystream" created by rsteam:

https://github.com/stomp-js/stompjs
rabbitmq/rabbitmq-server#3508

import { Client, Message } from '../assets/stomp.umd.min.js'

console.log("hello world stream")

const client = new Client({
    brokerURL: 'ws://127.0.0.1:15674/ws',
    connectHeaders: {
        login: 'guest',
        passcode: 'guest'
    },
    debug: function (str) {
        console.log(str);
    },
    reconnectDelay: 5000,
    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000,
})


client.onConnect = function (frame) {

    console.log('Connected');

    let callback = function (message) {
        const msg = message.body
        console.log('callback ', msg);
        message.ack()
    }

    var subscription = client.subscribe(
        "/amq/queue/mystream", //created via rstream demo                        error
        //"/amq/queue/s_test",  // created via http://127.0.0.1:15672/#/queues   OK
        callback,
        {
            'ack': 'client-individual',
            'durable': true,
            'auto-delete': false,
            'x-queue-type': 'stream',
            'prefetch-count': 10,
            'x-stream-offset': 'last'
        })
}

client.activate()

when create a stream and publish a message(without any header ) manually from web management http://127.0.0.1:15672/#/queues, it's ok.

but cant subscribe "mystream" created by rsteam,
rabbitMQ server raise error:

rabbitmq-stream_1 | 2022-03-10 08:34:49.742812+00:00 [info] <0.691.0> osiris_writer:init/1: name: __mystream_1646894235949435900 last offset: 99 committed chunk id: 99 epoch: 4
rabbitmq-stream_1 | 2022-03-10 08:35:44.302586+00:00 [warn] <0.784.0> AMQP 0-9-1 client call timeout was 70000 ms, is updated to a safe effective value of 130000 ms
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> ** Generic server <0.800.0> terminating
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> ** Last message in was {'$gen_cast',
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {queue_event,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {osiris_offset,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> 99}}}
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> ** When Server state == {ch,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {conf,running,rabbit_framing_amqp_0_9_1,1,<0.796.0>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> <0.796.0>,<0.788.0>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> <<"172.24.0.1:52798 -> 172.24.0.2:15674">>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {user,<<"guest">>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [administrator],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{rabbit_auth_backend_internal,none}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> <<"/">>,<<>>,<0.793.0>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{<<"publisher_confirms">>,bool,true},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {<<"exchange_exchange_bindings">>,bool,true},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {<<"basic.nack">>,bool,true},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {<<"consumer_cancel_notify">>,bool,true},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {<<"connection.blocked">>,bool,true},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {<<"authentication_failure_close">>,bool,true}],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> none,10,134217728,1800000,#{},1000000000},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {lstate,<0.799.0>,false},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> none,1,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {0,{[],[]}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {state,#{},erlang},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{<<"T_sub-0">> =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {{amqqueue,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> true,false,none,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{<<"x-queue-type">>,longstr,<<"stream">>}],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> <0.691.0>,[],[],[],undefined,undefined,[],[],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> live,0,[],<<"/">>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{user => <<"guest">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> rabbit_stream_queue,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{epoch => 4,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> event_formatter =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_stream_queue,format_osiris_event,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{resource,<<"/">>,queue,<<"mystream">>}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> leader_locator_strategy => <<"client-local">>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> leader_node => rabbit@e88fe41271a0,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> leader_pid => <0.691.0>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> name => "__mystream_1646894235949435900",
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> nodes => [rabbit@e88fe41271a0],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> reference =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> replica_nodes => [],retention => []}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {false,10,false,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{<<"x-stream-offset">>,longstr,<<"last">>}]}}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{{resource,<<"/">>,queue,<<"mystream">>} =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {1,{<<"T_sub-0">>,nil,nil}}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {state,fine,5000,#Ref<0.3039776074.1391984642.38321>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> false,1,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_confirms,undefined,#{}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [],[],none,flow,[],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_queue_type,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{{resource,<<"/">>,queue,<<"mystream">>} =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {ctx,rabbit_stream_queue,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {stream_client,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> "__mystream_1646894235949435900",
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> <0.691.0>,<0.691.0>,1,#{},256,false,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{<<"T_sub-0">> =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {stream,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {resource,<<"/">>,queue,<<"mystream">>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> 10,10,98,98,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {osiris_log,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {cfg,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> "/var/lib/rabbitmq/mnesia/rabbit@e88fe41271a0/stream/__mystream_1646894235949435900",
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> "__mystream_1646894235949435900",
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> 500000000,256000,#{},[],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {write_concurrency,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #Ref<0.3039776074.1392115715.36239>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined,#Fun<osiris_writer.4.53124714>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #Fun<osiris_log.4.129764537>},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {read,offset,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #Ref<0.3039776074.1392115713.41625>,0,99,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> tcp,user_data},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {file_descriptor,prim_file,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{handle =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #Ref<0.3039776074.1392115726.36064>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> owner => <0.800.0>,r_ahead_size => 0,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> r_buffer =>
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #Ref<0.3039776074.1392115715.36238>}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined}}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> <<"<0.800.0>_-7OviwPAH2WKsKPp8s-d-Bw">>}}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #{}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> #Ref<0.3039776074.1391984642.38317>,false}
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> ** Reason for termination ==
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> ** {function_clause,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{rabbit_msg_record,decode,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [[{'v1_0.header',undefined,undefined,undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {'v1_0.properties',undefined,undefined,undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined,undefined,undefined,undefined,undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> undefined,undefined,undefined},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {'v1_0.amqp_value',{utf8,<<"hello: 99">>}}],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {undefined,undefined,undefined,undefined}],
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{file,"rabbit_msg_record.erl"},{line,62}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_msg_record,init,1,[{file,"rabbit_msg_record.erl"},{line,54}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_stream_queue,binary_to_msg,2,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,915}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_stream_queue,'-stream_entries/4-lc$^0/1-0-',5,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,888}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_stream_queue,stream_entries,4,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,892}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_stream_queue,'-handle_event/2-fun-1-',5,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,401}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {maps,fold_1,3,[{file,"maps.erl"},{line,410}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> {rabbit_stream_queue,handle_event,2,
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,399}]}]}
rabbitmq-stream_1 | 2022-03-10 08:35:44.323451+00:00 [erro] <0.800.0>
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> crasher:
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> initial call: rabbit_channel:init/1
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> pid: <0.800.0>
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> registered_name: []
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> exception exit: {function_clause,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{rabbit_msg_record,decode,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [[{'v1_0.header',undefined,undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> undefined,undefined},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {'v1_0.properties',undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> undefined,undefined,undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> undefined,undefined,undefined,undefined,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> undefined,undefined,undefined},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {'v1_0.amqp_value',{utf8,<<"hello: 99">>}}],
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {undefined,undefined,undefined,undefined}],
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_msg_record.erl"},{line,62}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_msg_record,init,1,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_msg_record.erl"},{line,54}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_stream_queue,binary_to_msg,2,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,915}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_stream_queue,'-stream_entries/4-lc$^0/1-0-',
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> 5,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,888}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_stream_queue,stream_entries,4,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,892}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_stream_queue,'-handle_event/2-fun-1-',5,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,401}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {maps,fold_1,3,[{file,"maps.erl"},{line,410}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_stream_queue,handle_event,2,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"rabbit_stream_queue.erl"},{line,399}]}]}
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> in function gen_server2:terminate/3 (gen_server2.erl, line 1183)
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> ancestors: [<0.798.0>,rabbit_direct_client_sup,rabbit_sup,<0.228.0>]
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> message_queue_len: 0
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> messages: []
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> links: [<0.798.0>,<0.796.0>]
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> dictionary: [{process_name,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rabbit_channel,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {<<"172.24.0.1:52798 -> 172.24.0.2:15674">>,1}}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {permission_cache_can_expire,false},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {rand_seed,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {#{jump => #Fun<rand.3.92093067>,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> max => 288230376151711743,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> next => #Fun<rand.5.92093067>,type => exsplus},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [53738507667940155|90401130320299917]}},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {permission_cache,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{{resource,<<"/">>,queue,<<"mystream">>},#{},read}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {channel_operation_timeout,15000},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {guid,{{3974881984,4027046242,2888366716,3018292743},0}}]
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> trap_exit: true
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> status: running
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> heap_size: 4185
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> stack_size: 29
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> reductions: 88547
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> neighbours:
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> neighbour:
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> pid: <0.796.0>
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> registered_name: []
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> initial call: amqp_channel:init/1
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> current_function: {gen_server,loop,7}
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> ancestors: [<0.794.0>,<0.791.0>,<0.787.0>,<0.786.0>,amqp_sup,<0.530.0>]
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> message_queue_len: 0
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> links: [<0.794.0>,<0.800.0>,<0.784.0>]
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> trap_exit: false
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> status: waiting
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> heap_size: 610
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> stack_size: 12
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> reductions: 410
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> current_stacktrace: [{gen_server,loop,7,[{file,"gen_server.erl"},{line,443}]},
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> {proc_lib,init_p_do_apply,3,
rabbitmq-stream_1 | 2022-03-10 08:35:44.326067+00:00 [erro] <0.800.0> [{file,"proc_lib.erl"},{line,226}]}]

Bumping the uamqp library to last version

Using JRubics/[email protected] to publish on pypi seems like is complaining about the uampq library saying:

Note: This error originates from the build backend, and is likely not a problem with poetry but with uamqp (1.5.2) not supporting PEP 517 builds. You can verify this by running 'pip wheel --use-pep517 "uamqp (==1.5.2) ; python_version >= "3.6""'.

Probably is worth to bump it to the last version if tests are ok

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.