4个版本

0.1.3 2022年1月17日
0.1.2 2021年10月29日
0.1.1 2021年10月29日
0.1.0 2021年10月29日

#1440数据库接口

MIT许可证

105KB
2.5K SLoC

Rust 2K SLoC // 0.0% comments SQL 365 SLoC // 0.1% comments

Timesource

使用TimescaleDb作为存储引擎的事件源,适用于Rust应用程序。

注意:公共API非常不稳定。当API进一步稳定时,将发布文档。

特性

  • 具有TimescaleDb提供所有优势的事件源
    • 分区表,实现横向扩展
    • 查询速度比PostgreSQL快10-100倍
    • 压缩数据,降低存储需求
  • 可以追踪到事件发生的确切时刻。将来,此crate旨在提供时间旅行和时间感知仓库的API。所有事件都有纳秒级的时间戳。
  • Timesource采用灵活的事件序列化方法。它支持以下任一种格式:JSON、CBOR或Protocol Buffers
  • 具有强大保护措施的消费者
    • 如果数据库出现问题时,将应用背压
    • 消费者处理速度慢时,会自我修复并赶上事件,当新消息的速率大于处理它们所需的时间时
    • 偏移量存储在数据库中,以便消费者可以从上次离开的地方继续
  • 灵活的排序约束
    • 事件可以有序或无序地发布。但是,消费者始终会以有序的方式收到事件。
    • 同一聚合类型的事件按单调递增的整数排序。聚合根按时间戳单独排序。
  • 存储引擎可以处理总共9,223,372,036,854,775,807个事件,直到11 April 2262 23:47:16.854

事件序列化/反序列化

Timesource支持

如果没有提供配置,timesource默认使用JSON。编码可以通过派生属性进行配置。例如

#[derive(serde::Serialize, serde::Deserialize, TimesourceEvent, PartialEq, Debug)]
#[timesource(encoding = "json")] // this is optional, as JSON is already the default
enum TdbEventJson {
    Created,
    Abandoned(String),
    #[allow(dead_code)]
    AddedItem {
        id: usize,
    },
}

#[derive(minicbor::Encode, minicbor::Decode, TimesourceEvent, PartialEq, Debug)]
#[timesource(encoding = "cbor", version = "1.1")] // it's also possible to set the version along with the encoding
enum TdbEventCbor {
    #[b(0)]
    Created,
    #[b(1)]
    Abandoned(#[b(0)] String),
    #[b(2)]
    AddedItem {
        #[b(0)]
        id: usize,
    },
}

mod proto {
    use prost::Message;

    #[derive(Clone, PartialEq, Message)]
    pub struct Created {}
    #[derive(Clone, PartialEq, Message)]
    pub struct Abandoned {
        #[prost(string, tag = "1")]
        pub reason: ::prost::alloc::string::String,
    }
    #[derive(Clone, PartialEq, Message)]
    pub struct AddedItem {
        #[prost(uint32, tag = "1")]
        pub id: u32,
    }
    #[derive(Clone, PartialEq, Message, TimesourceEvent)]
    #[timesource(encoding = "proto")]
    pub struct TdbEvent {
        #[prost(oneof = "tdb_event_proto::Data", tags = "1, 2, 3")]
        pub data: ::core::option::Option<tdb_event_proto::Data>,
    }

    pub mod tdb_event_proto {
        #[derive(Clone, PartialEq, ::prost::Oneof)]
        pub enum Data {
            #[prost(message, tag = "1")]
            Created(super::Created),
            #[prost(message, tag = "2")]
            Abandoned(super::Abandoned),
            #[prost(message, tag = "3")]
            AddedItem(super::AddedItem),
        }
    }
}

每种支持的协议都有其优点和缺点,可能没有一种可以覆盖所有用例

以下表格总结了为什么选择每种协议

编码 人类友好(即易于阅读) 性能 存储大小 模式演化支持
JSON x (通过 derive 属性手动)
CBOR x x x
Protobuf x x x

尽管目前还没有关于 timesource 的基准测试,但检查 serdebench 的序列化基准测试可能会有所帮助。

与 Eventually 的区别

Timesource 是作为 eventually-rs 的分支而诞生的。Eventually-rs 不支持时间戳,因此无法将 timescaledb 作为存储引擎有意义地使用。

而 Eventually 的理念是成为一个具有可插拔存储引擎的事件源库,Timesource 只会支持 Timescaledb。这使得 Timesource 的范围更加狭窄。这也允许进行一些优化,更有效地处理事件,并消除 内存溢出 的风险。

如何使用

请参阅 订单聚合示例

依赖项

~20–31MB
~572K SLoC