如何向EventStore提交特定事件元数据?批量写入400报错求助
EventStore事件元数据提交与多租户过滤方案
我之前踩过EventStore元数据提交的坑,正好能帮你解决这两个问题:
一、正确的事件+元数据原子提交方式
你收到400错误的核心原因是EventStore的HTTP API要求data和metadata字段必须是base64编码的字符串,而不是直接的JSON对象——这是很容易忽略的细节!
单个事件的正确格式(HTTP API)
先把你的data和metadata对象序列化为JSON字符串,再做base64编码,最终请求体应该是这样:
{ "eventId": "47b9cc67-ebf5-4990-b95d-ef23e45ee520", "eventType": "CreateBadger", "data": "eyJiYWRnZXIiOiJtdXNob3JvbSIsIm11c2hyb29tIjoiYmFkZ2VyIn0=", "metadata": "eyJ0ZW5hbnRJZCI6IjVhZjUzOTk2LTAyMzMtNGZmZC1iNzFkLWY3NTBkMTI5NjVkNiIsInN1YnNjcmlwdGlvblRJZCI6ImI2MzMxNjYwLTIyZjctNGM3YS1hMzQ5LTgwNjFjOTE5NzNjZCIsInVzZXJJZCI6IjczZmNjZjQ3LTVkZTAtNGViMy1hOTI1LTdiYmJlMTc5MGEzNSJ9" }
批量事件的原子提交格式
批量提交只需把单个事件包装成数组即可,EventStore会保证这个请求是原子的——要么所有事件都写入成功,要么全部失败:
[ { "eventId": "47b9cc67-ebf5-4990-b95d-ef23e45ee520", "eventType": "CreateBadger", "data": "eyJiYWRnZXIiOiJtdXNob3JvbSIsIm11c2hyb29tIjoiYmFkZ2VyIn0=", "metadata": "eyJ0ZW5hbnRJZCI6IjVhZjUzOTk2LTAyMzMtNGZmZC1iNzFkLWY3NTBkMTI5NjVkNiIsInN1YnNjcmlwdGlvblRJZCI6ImI2MzMxNjYwLTIyZjctNGM3YS1hMzQ5LTgwNjFjOTE5NzNjZCIsInVzZXJJZCI6IjczZmNjZjQ3LTVkZTAtNGViMy1hOTI1LTdiYmJlMTc5MGEzNSJ9" }, { "eventId": "another-uuid-here", "eventType": "UpdateBadger", "data": "...base64-encoded-data...", "metadata": "...base64-encoded-metadata..." } ]
补充:gRPC API的情况
如果用的是gRPC API,就不需要base64编码了——直接把data和metadata序列化为字节数组传递即可,格式和你最初写的类似,这也是很多人混淆HTTP和gRPC格式的原因。
二、基于元数据实现多租户模式
完全可以实现,推荐两种方案,按需选择:
方案1:用投影预先路由租户事件(推荐)
通过EventStore的持续投影,可以自动将带有tenantId元数据的事件路由到专属的租户流(比如tenant-5af53996-0233-4ffd-b71d-f750d12965d6)。这样查询的时候直接读取对应租户的流,性能最优。
示例投影代码(JavaScript):
fromAll() .when({ $any: function(s, e) { if (e.metadata && e.metadata.tenantId) { linkTo(`tenant-${e.metadata.tenantId}`, e); } } })
这个投影会持续运行,把所有带租户元数据的事件链接到对应租户流,既不重复存储事件,又能实现租户隔离。
方案2:读取时过滤元数据
如果不想用投影,也可以在读取流时直接过滤元数据。比如:
- 使用.NET SDK时,可以通过
StreamReadFilter指定元数据过滤条件; - 使用HTTP API时,在读取流的请求中添加
metadata-filter-tenantId=your-tenant-id这类参数。
不过这种方式是在读取时实时过滤,数据量较大时性能会不如投影方案。
内容的提问来源于stack exchange,提问作者designermonkey
相关产品推荐
相关产品推荐

