Transaction, rollback the already wrote events

Each product has its own stream in which I store events about a decrease in the product’s stock or an increase. And I have a separate command that reduces the number of products when the order is successfully completed and delivered to the client.

The command algorithm is as follows:
Selects all products that were delivered, and for each stream(in cycle) for each product records an event that the product is reduced by some quantity(it’s also separate command with own validation to check cannot be reduced if there is not enough stock). This is where I have a problem, if the stream does not have the corresponding number of products, then my command will throw an error for each product, and I will need to roll back all previous events or probably other unexpected errors occurred. For example, about halfway through the cycle, my application server crashed and it turned out that half of the events were recorded and half were not yet, and as a result, all the records are not rolled back and unreliable data about the current stock of products remains. Since the goods were delivered, there were no fewer products.

How is it possible to provide rollback logic in EventStoreDb that rolls back all recorded events within 1 transaction for all streams where there were recordings?

It’s easy for me to implement this logic manually for 1 stream, but my command writes a lot of events in a cycle to different streams, and it’s difficult to do a rollback for all of them.

I suspect you need a reconcialiation process somewhere:

(I’m not certain I understand fully your use case )

you want to decrease the stock in certain streams of product that were deliverd but want to check , in those streams that the stock can not go under zero ?

if the product was delivered , then you can not deny that fact
the “not under zero” constraints seems strange to me .

What other parts of the system are adding / removing from the stock ?

Typically in those scenarios , the stock level in the system is not the real stock level ( because someone might pick 2 boxes instead of one an just scan one for example )

This might help :

Currently, there is no cross-stream transaction support. The atomicity is at the append to 1 stream level.

But … this seems more like a modeling issue. Perhaps you are missing the concept of a delivery or shipment of several products. Planning a delivery/shipment could result in either a pre-allocation or allocation of the products, thereby decreasing the stock level of each product. An orchestration could be used to perform the allocation of each product quantity. Any failure to allocate a product quantity could be used to roll back successful allocations that are part of the same shipment.
If the delivery/shipment gets canceled or returned, then you can also return the products and increase the stock level of each product.
As @yves.lorphelin points out, rejecting to decrease product stock level after a successful delivery makes little sense and recorded stock levels are but a digital reflection of the stock level in the real world, which can deviate for various reasons.