{"id":31921876,"url":"https://github.com/rmw-lib/expire_map","last_synced_at":"2025-10-13T22:55:18.423Z","repository":{"id":47127177,"uuid":"515896774","full_name":"rmw-lib/expire_map","owner":"rmw-lib","description":null,"archived":false,"fork":false,"pushed_at":"2022-07-26T03:36:11.000Z","size":79,"stargazers_count":8,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-21T07:07:35.575Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rmw-lib.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-07-20T08:27:15.000Z","updated_at":"2025-04-10T04:54:25.000Z","dependencies_parsed_at":"2022-09-16T09:01:52.224Z","dependency_job_id":null,"html_url":"https://github.com/rmw-lib/expire_map","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":"rmw-lib/rust_tmpl","purl":"pkg:github/rmw-lib/expire_map","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmw-lib%2Fexpire_map","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmw-lib%2Fexpire_map/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmw-lib%2Fexpire_map/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmw-lib%2Fexpire_map/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rmw-lib","download_url":"https://codeload.github.com/rmw-lib/expire_map/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmw-lib%2Fexpire_map/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017087,"owners_count":26085984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-13T22:55:10.773Z","updated_at":"2025-10-13T22:55:18.409Z","avatar_url":"https://github.com/rmw-lib.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- EDIT /Users/z/rmw/expire_map/README.md --\u003e\n\n# expire_map\n\n\u003ca href=\"https://docs.rs/expire_map\"\u003e\u003cimg src=\"https://img.shields.io/badge/RUST-API%20DOC-blue?style=for-the-badge\u0026logo=docs.rs\u0026labelColor=333\" alt=\"Api Doc\"\u003e\u003c/a\u003e\n\n[English](#english-readme) | [中文说明](#中文说明)\n\n---\n\n## English Readme\n\n\u003c!-- EDIT /Users/z/rmw/expire_map/doc/en/readme.md --\u003e\n\n## Use\n\n`expire_map` : High concurrency timeout dictionary supporting a maximum of 256 cycles timeout (internally implemented using dashmap).\n\nUnlike the existing rust expire map, there is context object in the parameters of the timeout callback, which avoids wasting memory space for context pointers in each timeout object.\n\nAlso, I implement RetryMap based on ExpireMap and can be used for network request timeouts and retries.\n\n## RetryMap usage demo\n\n[→ examples/main.rs](examples/main.rs)\n\n```rust\nuse std::{\n  net::{Ipv4Addr, SocketAddrV4, UdpSocket},\n  time::Duration,\n};\n\nuse anyhow::Result;\nuse async_std::task::{block_on, sleep, spawn};\nuse expire_map::{Caller, RetryMap};\n\n#[derive(Debug)]\nstruct Msg {\n  msg: Box\u003c[u8]\u003e,\n}\n\n#[derive(Copy, Clone, Eq, Hash, PartialEq)]\nstruct Task {\n  addr: SocketAddrV4,\n  id: u16,\n}\n\nstruct Db {}\nimpl Db {\n  pub fn insert(\u0026self, addr: SocketAddrV4, msg: impl AsRef\u003c[u8]\u003e) {\n    println!(\"fail {} {:?}\", addr, msg.as_ref());\n  }\n}\n\nstruct Ctx {\n  udp: UdpSocket,\n  db: Db,\n}\n\nimpl Ctx {\n  fn new() -\u003e Result\u003cSelf\u003e {\n    let udp = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))?;\n    Ok(Self { udp, db: Db {} })\n  }\n}\n\nimpl Caller\u003cCtx, Task\u003e for Msg {\n  fn ttl() -\u003e u8 {\n    2 // expire after 2 seconds\n  }\n\n  fn call(\u0026mut self, ctx: \u0026Ctx, task: \u0026Task) -\u003e u8 {\n    let cmd = format!(\"{} {}#{} {:?}\", \"call\", task.addr, task.id, \u0026self.msg);\n    if let Err(err) = ctx.udp.send_to(\n      \u0026[\u0026task.id.to_le_bytes()[..], \u0026self.msg[..]].concat(),\n      task.addr,\n    ) {\n      dbg!(err);\n    }\n    dbg!(cmd);\n    Self::ttl()\n  }\n\n  fn fail(\u0026mut self, ctx: \u0026Ctx, task: \u0026Task) {\n    ctx.db.insert(task.addr, \u0026self.msg)\n  }\n}\n\nfn main() -\u003e Result\u003c()\u003e {\n  let ctx = Ctx::new()?;\n  let retry_map = RetryMap::new(ctx);\n\n  let msg = Msg {\n    msg: Box::from(\u0026[1, 2, 3][..]),\n  };\n\n  let task = Task {\n    id: 12345,\n    addr: \"223.5.5.5:53\".parse()?,\n  };\n\n  let retry_times = 3; // 重试次数是3次\n\n  let expireer = retry_map.clone();\n\n  let handle = spawn(async move {\n    let mut do_expire = 0;\n    loop {\n      sleep(Duration::from_secs(1)).await;\n      expireer.do_expire();\n      do_expire += 1;\n      let exist = expireer.get(\u0026task).is_some();\n      println!(\"{} {}\", do_expire, exist);\n    }\n  });\n\n  // will run call() when insert\n  retry_map.insert(task, msg, retry_times);\n\n  retry_map.renew(task, 5);\n  //dbg!(retry_map.get(\u0026task).unwrap().value());\n  //dbg!(retry_map.get_mut(\u0026task).unwrap().key());\n  //retry_map.remove(task);\n\n  block_on(handle);\n  Ok(())\n}\n```\n\n\nOutput\n\n[→ out.txt](out.txt)\n\n```txt\n+ ./sh/run.sh --example main\n+ exec cargo run --example main\n   Compiling expire_map v0.0.26 (/Users/z/rmw/expire_map)\n    Finished dev [unoptimized + debuginfo] target(s) in 1.77s\n     Running `/Users/z/rmw/expire_map/target/debug/examples/main`\n[examples/main.rs:53] cmd = \"call 223.5.5.5:53#12345 [1, 2, 3]\"\n1 true\n2 true\n3 true\n4 true\n5 true\n[examples/main.rs:53] cmd = \"call 223.5.5.5:53#12345 [1, 2, 3]\"\n6 true\n7 true\n[examples/main.rs:53] cmd = \"call 223.5.5.5:53#12345 [1, 2, 3]\"\n8 true\n9 true\nfail 223.5.5.5:53 [1, 2, 3]\n10 false\n11 false\n12 false\n13 false\n14 false\n15 false\n```\n\n\n## ExpireMap usage demo\n\nThe use of ExpireMap can be seen in the RetryMap implementation\n\n[→ src/retry.rs](src/retry.rs)\n\n```rust\nuse std::{default::Default, ops::Deref};\n\nuse crate::{expire_map::Key, ExpireMap, OnExpire};\n\npub trait Caller\u003cCtx, K\u003e {\n  /// Time-To-Live\n  fn ttl() -\u003e u8;\n  fn call(\u0026mut self, ctx: \u0026Ctx, key: \u0026K) -\u003e u8;\n  fn fail(\u0026mut self, ctx: \u0026Ctx, key: \u0026K);\n}\n\n#[derive(Default)]\npub struct Retry\u003cC\u003e {\n  n: u8,\n  caller: C,\n}\n\nimpl\u003cCtx, K, C: Caller\u003cCtx, K\u003e\u003e OnExpire\u003cCtx, K\u003e for Retry\u003cC\u003e {\n  fn on_expire(\u0026mut self, ctx: \u0026Ctx, key: \u0026K) -\u003e u8 {\n    let n = self.n.wrapping_sub(1);\n    if n == 0 {\n      self.caller.fail(ctx, key);\n      0\n    } else {\n      self.n = n;\n      self.caller.call(ctx, key)\n    }\n  }\n}\n\npub trait Task\u003cCtx, K\u003e = Caller\u003cCtx, K\u003e;\n\npub struct RetryMap\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e {\n  pub expire: ExpireMap\u003cCtx, K, Retry\u003cC\u003e\u003e,\n}\n\nimpl\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e Clone for RetryMap\u003cCtx, K, C\u003e {\n  fn clone(\u0026self) -\u003e Self {\n    Self {\n      expire: self.expire.clone(),\n    }\n  }\n}\n\nimpl\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e RetryMap\u003cCtx, K, C\u003e {\n  pub fn new(ctx: Ctx) -\u003e Self {\n    Self {\n      expire: ExpireMap::new(ctx),\n    }\n  }\n  pub fn remove(\u0026self, key: K) -\u003e Option\u003cC\u003e {\n    if let Some(r) = self.expire.remove(key) {\n      Some(r.caller)\n    } else {\n      None\n    }\n  }\n\n  pub fn insert(\u0026self, key: K, mut caller: C, retry: u8) {\n    caller.call(\u0026self.ctx, \u0026key);\n    self\n      .expire\n      .insert(key, Retry { n: retry, caller }, C::ttl());\n  }\n}\n\nimpl\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e Deref for RetryMap\u003cCtx, K, C\u003e {\n  type Target = ExpireMap\u003cCtx, K, Retry\u003cC\u003e\u003e;\n  fn deref(\u0026self) -\u003e \u0026\u003cSelf as Deref\u003e::Target {\n    \u0026self.expire\n  }\n}\n```\n\n\n### About\n\nThis project is part of **[rmw.link](//rmw.link)** Code Project\n\n![rmw.link logo](https://raw.githubusercontent.com/rmw-link/logo/master/rmw.red.bg.svg)\n\n---\n\n## 中文说明\n\n\u003c!-- EDIT /Users/z/rmw/expire_map/doc/zh/readme.md --\u003e\n\n`expire_map` : 最大支持 256 个周期超时的高并发字典（内部使用 dashmap 实现）。\n\n和现有的 rust 超时字典不同，在超时回调的参数中有上下文对象，这样可以避免在每个超时对象中浪费内存空间放置上下文指针。\n\n同时，我还基于 ExpireMap 实现了 RetryMap，可以用于网络请求超时和重试。\n\n## RetryMap 使用演示\n\n[→ examples/main.rs](examples/main.rs)\n\n```rust\nuse std::{\n  net::{Ipv4Addr, SocketAddrV4, UdpSocket},\n  time::Duration,\n};\n\nuse anyhow::Result;\nuse async_std::task::{block_on, sleep, spawn};\nuse expire_map::{Caller, RetryMap};\n\n#[derive(Debug)]\nstruct Msg {\n  msg: Box\u003c[u8]\u003e,\n}\n\n#[derive(Copy, Clone, Eq, Hash, PartialEq)]\nstruct Task {\n  addr: SocketAddrV4,\n  id: u16,\n}\n\nstruct Db {}\nimpl Db {\n  pub fn insert(\u0026self, addr: SocketAddrV4, msg: impl AsRef\u003c[u8]\u003e) {\n    println!(\"fail {} {:?}\", addr, msg.as_ref());\n  }\n}\n\nstruct Ctx {\n  udp: UdpSocket,\n  db: Db,\n}\n\nimpl Ctx {\n  fn new() -\u003e Result\u003cSelf\u003e {\n    let udp = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))?;\n    Ok(Self { udp, db: Db {} })\n  }\n}\n\nimpl Caller\u003cCtx, Task\u003e for Msg {\n  fn ttl() -\u003e u8 {\n    2 // expire after 2 seconds\n  }\n\n  fn call(\u0026mut self, ctx: \u0026Ctx, task: \u0026Task) -\u003e u8 {\n    let cmd = format!(\"{} {}#{} {:?}\", \"call\", task.addr, task.id, \u0026self.msg);\n    if let Err(err) = ctx.udp.send_to(\n      \u0026[\u0026task.id.to_le_bytes()[..], \u0026self.msg[..]].concat(),\n      task.addr,\n    ) {\n      dbg!(err);\n    }\n    dbg!(cmd);\n    Self::ttl()\n  }\n\n  fn fail(\u0026mut self, ctx: \u0026Ctx, task: \u0026Task) {\n    ctx.db.insert(task.addr, \u0026self.msg)\n  }\n}\n\nfn main() -\u003e Result\u003c()\u003e {\n  let ctx = Ctx::new()?;\n  let retry_map = RetryMap::new(ctx);\n\n  let msg = Msg {\n    msg: Box::from(\u0026[1, 2, 3][..]),\n  };\n\n  let task = Task {\n    id: 12345,\n    addr: \"223.5.5.5:53\".parse()?,\n  };\n\n  let retry_times = 3; // 重试次数是3次\n\n  let expireer = retry_map.clone();\n\n  let handle = spawn(async move {\n    let mut do_expire = 0;\n    loop {\n      sleep(Duration::from_secs(1)).await;\n      expireer.do_expire();\n      do_expire += 1;\n      let exist = expireer.get(\u0026task).is_some();\n      println!(\"{} {}\", do_expire, exist);\n    }\n  });\n\n  // will run call() when insert\n  retry_map.insert(task, msg, retry_times);\n\n  retry_map.renew(task, 5);\n  //dbg!(retry_map.get(\u0026task).unwrap().value());\n  //dbg!(retry_map.get_mut(\u0026task).unwrap().key());\n  //retry_map.remove(task);\n\n  block_on(handle);\n  Ok(())\n}\n```\n\n\n运行输出\n\n[→ out.txt](out.txt)\n\n```txt\n+ ./sh/run.sh --example main\n+ exec cargo run --example main\n   Compiling expire_map v0.0.26 (/Users/z/rmw/expire_map)\n    Finished dev [unoptimized + debuginfo] target(s) in 1.77s\n     Running `/Users/z/rmw/expire_map/target/debug/examples/main`\n[examples/main.rs:53] cmd = \"call 223.5.5.5:53#12345 [1, 2, 3]\"\n1 true\n2 true\n3 true\n4 true\n5 true\n[examples/main.rs:53] cmd = \"call 223.5.5.5:53#12345 [1, 2, 3]\"\n6 true\n7 true\n[examples/main.rs:53] cmd = \"call 223.5.5.5:53#12345 [1, 2, 3]\"\n8 true\n9 true\nfail 223.5.5.5:53 [1, 2, 3]\n10 false\n11 false\n12 false\n13 false\n14 false\n15 false\n```\n\n\n## ExpireMap 使用演示\n\nExpireMap 的使用可以参见 RetryMap 的实现\n\n[→ src/retry.rs](src/retry.rs)\n\n```rust\nuse std::{default::Default, ops::Deref};\n\nuse crate::{expire_map::Key, ExpireMap, OnExpire};\n\npub trait Caller\u003cCtx, K\u003e {\n  /// Time-To-Live\n  fn ttl() -\u003e u8;\n  fn call(\u0026mut self, ctx: \u0026Ctx, key: \u0026K) -\u003e u8;\n  fn fail(\u0026mut self, ctx: \u0026Ctx, key: \u0026K);\n}\n\n#[derive(Default)]\npub struct Retry\u003cC\u003e {\n  n: u8,\n  caller: C,\n}\n\nimpl\u003cCtx, K, C: Caller\u003cCtx, K\u003e\u003e OnExpire\u003cCtx, K\u003e for Retry\u003cC\u003e {\n  fn on_expire(\u0026mut self, ctx: \u0026Ctx, key: \u0026K) -\u003e u8 {\n    let n = self.n.wrapping_sub(1);\n    if n == 0 {\n      self.caller.fail(ctx, key);\n      0\n    } else {\n      self.n = n;\n      self.caller.call(ctx, key)\n    }\n  }\n}\n\npub trait Task\u003cCtx, K\u003e = Caller\u003cCtx, K\u003e;\n\npub struct RetryMap\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e {\n  pub expire: ExpireMap\u003cCtx, K, Retry\u003cC\u003e\u003e,\n}\n\nimpl\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e Clone for RetryMap\u003cCtx, K, C\u003e {\n  fn clone(\u0026self) -\u003e Self {\n    Self {\n      expire: self.expire.clone(),\n    }\n  }\n}\n\nimpl\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e RetryMap\u003cCtx, K, C\u003e {\n  pub fn new(ctx: Ctx) -\u003e Self {\n    Self {\n      expire: ExpireMap::new(ctx),\n    }\n  }\n  pub fn remove(\u0026self, key: K) -\u003e Option\u003cC\u003e {\n    if let Some(r) = self.expire.remove(key) {\n      Some(r.caller)\n    } else {\n      None\n    }\n  }\n\n  pub fn insert(\u0026self, key: K, mut caller: C, retry: u8) {\n    caller.call(\u0026self.ctx, \u0026key);\n    self\n      .expire\n      .insert(key, Retry { n: retry, caller }, C::ttl());\n  }\n}\n\nimpl\u003cCtx, K: Key, C: Task\u003cCtx, K\u003e\u003e Deref for RetryMap\u003cCtx, K, C\u003e {\n  type Target = ExpireMap\u003cCtx, K, Retry\u003cC\u003e\u003e;\n  fn deref(\u0026self) -\u003e \u0026\u003cSelf as Deref\u003e::Target {\n    \u0026self.expire\n  }\n}\n```\n\n\n### 关于\n\n本项目隶属于 **人民网络 ([rmw.link](//rmw.link))** 代码计划。\n\n![人民网络海报](https://raw.githubusercontent.com/rmw-link/logo/master/rmw.red.bg.svg)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frmw-lib%2Fexpire_map","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frmw-lib%2Fexpire_map","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frmw-lib%2Fexpire_map/lists"}