├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── basic.rs ├── src └── lib.rs └── tests └── basic.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.log 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "rs-snowflake" 7 | version = "0.6.0" 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rs-snowflake" 3 | version = "0.6.0" 4 | authors = ["BinChengZhao "] 5 | edition = "2018" 6 | repository = "https://github.com/BinChengZhao/snowflake-rs.git" 7 | documentation = "https://docs.rs/rs-snowflake" 8 | readme = "README.md" 9 | description = "Rust version of the Twitter snowflake algorithm." 10 | keywords = [ "snowflake", "distributed-id", "speediness", "uniqueness", "auto-increment" ] 11 | categories = ["development-tools", "data-structures", "algorithms"] 12 | 13 | license = "MIT" 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [lib] 18 | name = "snowflake" 19 | 20 | [dependencies] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 BinCheng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snowflake-rs 2 | Rust version of the `Twitter snowflake algorithm` . 3 | 4 | A crate for quick generating distributed-ids. 5 | 6 | 7 | API Docs: https://docs.rs/rs-snowflake 8 | 9 | ## Usage 10 | 11 | Add this to your Cargo.toml: 12 | 13 | ```toml 14 | [dependencies] 15 | rs-snowflake = "*" 16 | ``` 17 | 18 | ## Getting Started 19 | 20 | ```rust 21 | use snowflake::SnowflakeIdGenerator; 22 | fn main() { 23 | let mut id_generator_generator = SnowflakeIdGenerator::new(1, 1); 24 | let id = id_generator_generator.real_time_generate(); 25 | } 26 | ``` 27 | 28 | ```rust 29 | use snowflake::SnowflakeIdBucket; 30 | fn main() { 31 | let mut id_generator_bucket = SnowflakeIdBucket::new(1, 1); 32 | let id = id_generator_bucket.get_id(); 33 | } 34 | ``` 35 | 36 | 37 | 38 | ``` 39 | test bench_generate_get_id_by_bucket ... bench: 5 ns/iter (+/- 0) 40 | 41 | test bench_generate_get_id_by_generator_general_version ... bench: 232 ns/iter (+/- 32) 42 | 43 | test bench_generate_get_id_by_generator_lazy_version ... bench: 2 ns/iter (+/- 0) 44 | 45 | test bench_generate_get_id_by_generator_real_time_version ... bench: 249 ns/iter (+/- 22) 46 | 47 | test bench_generate_ids_by_bucket ... bench: 13,077 ns/iter (+/- 1,263) 48 | 49 | ``` 50 | 51 | ## License 52 | 53 | Licensed under 54 | 55 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 56 | 57 | 58 | ### Contribution 59 | 60 | Thank you all very much for your contributions to the project, and if there is anything I can do to help, I would love to help! 61 | -------------------------------------------------------------------------------- /benches/basic.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | extern crate test; 3 | 4 | use snowflake::{SnowflakeIdBucket, SnowflakeIdGenerator}; 5 | use test::Bencher; 6 | 7 | #[bench] 8 | fn bench_generate_get_id_by_bucket(b: &mut Bencher) { 9 | let mut snowflake_id_bucket = SnowflakeIdBucket::new(1, 1); 10 | b.iter(|| snowflake_id_bucket.get_id()); 11 | } 12 | 13 | #[bench] 14 | fn bench_generate_get_id_by_generator_lazy_version(b: &mut Bencher) { 15 | let mut snowflake_id_generator = SnowflakeIdGenerator::new(1, 1); 16 | b.iter(|| snowflake_id_generator.lazy_generate()); 17 | } 18 | 19 | #[bench] 20 | fn bench_generate_get_id_by_generator_general_version(b: &mut Bencher) { 21 | let mut snowflake_id_generator = SnowflakeIdGenerator::new(1, 1); 22 | b.iter(|| snowflake_id_generator.generate()); 23 | } 24 | 25 | #[bench] 26 | fn bench_generate_get_id_by_generator_real_time_version(b: &mut Bencher) { 27 | let mut snowflake_id_generator = SnowflakeIdGenerator::new(1, 1); 28 | b.iter(|| snowflake_id_generator.real_time_generate()); 29 | } 30 | 31 | // #[bench] 32 | // fn bench_generate_ids_by_bucket(b: &mut Bencher) { 33 | // let mut snowflake_id_bucket = SnowflakeIdBucket::new(1, 1); 34 | // b.iter(|| snowflake_id_bucket.generate_ids()); 35 | // } 36 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Rust version of the `Twitter snowflake algorithm` . 2 | //! 3 | 4 | use std::hint::spin_loop; 5 | use std::time::{SystemTime, UNIX_EPOCH}; 6 | 7 | /// The `SnowflakeIdGenerator` type is snowflake algorithm wrapper. 8 | #[derive(Copy, Clone, Debug)] 9 | pub struct SnowflakeIdGenerator { 10 | /// epoch used by the snowflake algorithm. 11 | epoch: SystemTime, 12 | 13 | /// last_time_millis, last time generate id is used times millis. 14 | last_time_millis: i64, 15 | 16 | /// machine_id, is use to supplement id machine or sectionalization attribute. 17 | pub machine_id: i32, 18 | 19 | /// node_id, is use to supplement id machine-node attribute. 20 | pub node_id: i32, 21 | 22 | /// auto-increment record. 23 | idx: u16, 24 | } 25 | 26 | /// The `SnowflakeIdBucket` type is snowflake-id-bucket it easy to get id also have a id buffer. 27 | #[derive(Clone, Debug)] 28 | pub struct SnowflakeIdBucket { 29 | /// Hidden the `SnowflakeIdGenerator` in bucket . 30 | snowflake_id_generator: SnowflakeIdGenerator, 31 | 32 | /// The bucket buffer; 33 | bucket: Vec, 34 | } 35 | 36 | impl SnowflakeIdGenerator { 37 | /// Constructs a new `SnowflakeIdGenerator` using the UNIX epoch. 38 | /// Please make sure that machine_id and node_id is small than 32(2^5); 39 | /// 40 | /// # Examples 41 | /// 42 | /// ``` 43 | /// use snowflake::SnowflakeIdGenerator; 44 | /// 45 | /// let id_generator = SnowflakeIdGenerator::new(1, 1); 46 | /// ``` 47 | pub fn new(machine_id: i32, node_id: i32) -> SnowflakeIdGenerator { 48 | Self::with_epoch(machine_id, node_id, UNIX_EPOCH) 49 | } 50 | 51 | /// Constructs a new `SnowflakeIdGenerator` using the specified epoch. 52 | /// Please make sure that machine_id and node_id is small than 32(2^5); 53 | /// 54 | /// # Examples 55 | /// 56 | /// ``` 57 | /// use std::time::{Duration, UNIX_EPOCH}; 58 | /// use snowflake::SnowflakeIdGenerator; 59 | /// 60 | /// // 1 January 2015 00:00:00 61 | /// let discord_epoch = UNIX_EPOCH + Duration::from_millis(1420070400000); 62 | /// let id_generator = SnowflakeIdGenerator::with_epoch(1, 1, discord_epoch); 63 | /// ``` 64 | pub fn with_epoch(machine_id: i32, node_id: i32, epoch: SystemTime) -> SnowflakeIdGenerator { 65 | //TODO:limit the maximum of input args machine_id and node_id 66 | let last_time_millis = get_time_millis(epoch); 67 | 68 | SnowflakeIdGenerator { 69 | epoch, 70 | last_time_millis, 71 | machine_id, 72 | node_id, 73 | idx: 0, 74 | } 75 | } 76 | 77 | /// The real_time_generate keep id generate time is eq call method time. 78 | /// 79 | /// # Examples 80 | /// 81 | /// ``` 82 | /// use snowflake::SnowflakeIdGenerator; 83 | /// 84 | /// let mut id_generator = SnowflakeIdGenerator::new(1, 1); 85 | /// id_generator.real_time_generate(); 86 | /// ``` 87 | pub fn real_time_generate(&mut self) -> i64 { 88 | self.idx = (self.idx + 1) % 4096; 89 | 90 | let mut now_millis = get_time_millis(self.epoch); 91 | 92 | // supplement code for 'clock is moving backwards situation'. 93 | 94 | // If the milliseconds of the current clock are equal to 95 | // the number of milliseconds of the most recently generated id, 96 | // then check if enough 4096 are generated, 97 | // if enough then busy wait until the next millisecond. 98 | if now_millis == self.last_time_millis { 99 | if self.idx == 0 { 100 | now_millis = biding_time_conditions(self.last_time_millis, self.epoch); 101 | self.last_time_millis = now_millis; 102 | } 103 | } else { 104 | self.last_time_millis = now_millis; 105 | self.idx = 0; 106 | } 107 | 108 | // last_time_millis is 64 bits,left shift 22 bit,store 42 bits , machine_id left shift 17 bits, 109 | // node_id left shift 12 bits ,idx complementing bits. 110 | self.last_time_millis << 22 111 | | ((self.machine_id << 17) as i64) 112 | | ((self.node_id << 12) as i64) 113 | | (self.idx as i64) 114 | } 115 | 116 | /// The basic guarantee time punctuality. 117 | /// 118 | /// Basic guarantee time punctuality. 119 | /// sometimes one millis can't use up 4096 ID, the property of the ID isn't real-time. 120 | /// But setting time after every 4096 calls. 121 | /// # Examples 122 | /// 123 | /// ``` 124 | /// use snowflake::SnowflakeIdGenerator; 125 | /// 126 | /// let mut id_generator = SnowflakeIdGenerator::new(1, 1); 127 | /// id_generator.generate(); 128 | /// ``` 129 | pub fn generate(&mut self) -> i64 { 130 | self.idx = (self.idx + 1) % 4096; 131 | 132 | // Maintenance `last_time_millis` for every 4096 ids generated. 133 | if self.idx == 0 { 134 | let mut now_millis = get_time_millis(self.epoch); 135 | 136 | if now_millis == self.last_time_millis { 137 | now_millis = biding_time_conditions(self.last_time_millis, self.epoch); 138 | } 139 | 140 | self.last_time_millis = now_millis; 141 | } 142 | 143 | //last_time_millis is 64 bits,left shift 22 bit,store 42 bits , machine_id left shift 17 bits, 144 | //node_id left shift 12 bits ,idx complementing bits. 145 | self.last_time_millis << 22 146 | | ((self.machine_id << 17) as i64) 147 | | ((self.node_id << 12) as i64) 148 | | (self.idx as i64) 149 | } 150 | 151 | /// The lazy generate. 152 | /// 153 | /// Lazy generate. 154 | /// Just start time record last_time_millis it consume every millis ID. 155 | /// Maybe faster than standing time. 156 | /// # Examples 157 | /// 158 | /// ``` 159 | /// use snowflake::SnowflakeIdGenerator; 160 | /// 161 | /// let mut id_generator = SnowflakeIdGenerator::new(1, 1); 162 | /// id_generator.lazy_generate(); 163 | /// ``` 164 | pub fn lazy_generate(&mut self) -> i64 { 165 | self.idx = (self.idx + 1) % 4096; 166 | 167 | if self.idx == 0 { 168 | self.last_time_millis += 1; 169 | } 170 | 171 | self.last_time_millis << 22 172 | | ((self.machine_id << 17) as i64) 173 | | ((self.node_id << 12) as i64) 174 | | (self.idx as i64) 175 | } 176 | } 177 | 178 | impl SnowflakeIdBucket { 179 | /// Constructs a new `SnowflakeIdBucket` using the UNIX epoch. 180 | /// Please make sure that machine_id and node_id is small than 32(2^5); 181 | /// 182 | /// # Examples 183 | /// 184 | /// ``` 185 | /// use snowflake::SnowflakeIdBucket; 186 | /// 187 | /// let id_generator_bucket = SnowflakeIdBucket::new(1, 1); 188 | /// ``` 189 | pub fn new(machine_id: i32, node_id: i32) -> Self { 190 | Self::with_epoch(machine_id, node_id, UNIX_EPOCH) 191 | } 192 | 193 | /// Constructs a new `SnowflakeIdBucket` using the specified epoch. 194 | /// Please make sure that machine_id and node_id is small than 32(2^5); 195 | /// 196 | /// # Examples 197 | /// 198 | /// ``` 199 | /// use std::time::{Duration, UNIX_EPOCH}; 200 | /// use snowflake::SnowflakeIdBucket; 201 | /// 202 | /// // 1 January 2015 00:00:00 203 | /// let discord_epoch = UNIX_EPOCH + Duration::from_millis(1420070400000); 204 | /// let id_generator_bucket = SnowflakeIdBucket::with_epoch(1, 1, discord_epoch); 205 | /// ``` 206 | pub fn with_epoch(machine_id: i32, node_id: i32, epoch: SystemTime) -> Self { 207 | let snowflake_id_generator = SnowflakeIdGenerator::with_epoch(machine_id, node_id, epoch); 208 | let bucket = Vec::new(); 209 | 210 | SnowflakeIdBucket { 211 | snowflake_id_generator, 212 | bucket, 213 | } 214 | } 215 | 216 | /// Generate id. 217 | /// 218 | /// # Examples 219 | /// 220 | /// ``` 221 | /// use snowflake::SnowflakeIdBucket; 222 | /// 223 | /// let mut id_generator_bucket = SnowflakeIdBucket::new(1, 1); 224 | /// let id = id_generator_bucket.get_id(); 225 | /// 226 | /// ``` 227 | pub fn get_id(&mut self) -> i64 { 228 | // 247 ns/iter 229 | // after self.bucket.push(self.snowflake_id_generator.generate()); 230 | 231 | // 7 ns/iter 232 | // after self.bucket.push(self.snowflake_id_generator.lazy_generate()); 233 | 234 | //500 ns/iter 235 | // after self.bucket.push(self.snowflake_id_generator.real_time_generate()); 236 | if self.bucket.is_empty() { 237 | self.generate_ids(); 238 | } 239 | self.bucket.pop().unwrap() 240 | } 241 | 242 | fn generate_ids(&mut self) { 243 | // 30,350 -- 50,000 ns/iter 244 | //self.bucket.push(self.snowflake_id_generator.lazy_generate()); 245 | 246 | // 1,107,103 -- 1,035,018 ns/iter 247 | //self.bucket.push(self.snowflake_id_generator.generate()); 248 | 249 | // 2,201,325 -- 2,082,187 ns/iter 250 | //self.bucket.push(self.snowflake_id_generator.real_time_generate()); 251 | 252 | for _ in 0..4091 { 253 | self.bucket 254 | .push(self.snowflake_id_generator.lazy_generate()); 255 | } 256 | } 257 | } 258 | 259 | #[inline(always)] 260 | /// Get the latest milliseconds of the clock. 261 | pub fn get_time_millis(epoch: SystemTime) -> i64 { 262 | SystemTime::now() 263 | .duration_since(epoch) 264 | .expect("Time went mackward") 265 | .as_millis() as i64 266 | } 267 | 268 | #[inline(always)] 269 | // Constantly refreshing the latest milliseconds by busy waiting. 270 | fn biding_time_conditions(last_time_millis: i64, epoch: SystemTime) -> i64 { 271 | let mut latest_time_millis: i64; 272 | loop { 273 | latest_time_millis = get_time_millis(epoch); 274 | if latest_time_millis > last_time_millis { 275 | return latest_time_millis; 276 | } 277 | spin_loop(); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /tests/basic.rs: -------------------------------------------------------------------------------- 1 | use snowflake::SnowflakeIdGenerator; 2 | 3 | #[test] 4 | fn test_generate() { 5 | let mut id_generator = SnowflakeIdGenerator::new(1, 2); 6 | let mut ids = Vec::with_capacity(10000); 7 | 8 | for _ in 0..99 { 9 | for _ in 0..10000 { 10 | ids.push(id_generator.generate()); 11 | } 12 | 13 | ids.sort(); 14 | ids.dedup(); 15 | 16 | assert_eq!(10000, ids.len()); 17 | println!("{}", ids[9999]); 18 | 19 | ids.clear(); 20 | } 21 | } 22 | 23 | #[test] 24 | fn test_real_time_generate() { 25 | let mut id_generator = SnowflakeIdGenerator::new(2, 3); 26 | let mut ids = Vec::with_capacity(10000); 27 | 28 | for _ in 0..99 { 29 | for _ in 0..10000 { 30 | ids.push(id_generator.real_time_generate()); 31 | } 32 | 33 | ids.sort(); 34 | ids.dedup(); 35 | 36 | assert_eq!(10000, ids.len()); 37 | println!("{}", ids[9999]); 38 | 39 | ids.clear(); 40 | } 41 | } 42 | 43 | #[test] 44 | fn test_lazy_generate() { 45 | let mut id_generator = SnowflakeIdGenerator::new(3, 3); 46 | let mut ids = Vec::with_capacity(10000); 47 | 48 | for _ in 0..99 { 49 | for _ in 0..10000 { 50 | ids.push(id_generator.lazy_generate()); 51 | } 52 | 53 | ids.sort(); 54 | ids.dedup(); 55 | 56 | assert_eq!(10000, ids.len()); 57 | println!("{}", ids[9999]); 58 | 59 | ids.clear(); 60 | } 61 | } 62 | --------------------------------------------------------------------------------