github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/sawtooth-core-master/perf/sawtooth_perf/src/workload.rs (about) 1 /* 2 * Copyright 2018 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * ------------------------------------------------------------------------------ 16 */ 17 18 /// Tools for interacting with the Sawtooth Rest API 19 use std::cell::RefCell; 20 use std::error; 21 use std::fmt; 22 use std::io; 23 use std::iter::Cycle; 24 use std::rc::Rc; 25 use std::str::FromStr; 26 use std::sync::atomic::{AtomicUsize, Ordering}; 27 use std::time; 28 use std::vec::IntoIter; 29 30 use chrono; 31 use futures::Future; 32 use hyper::client::{Client, HttpConnector, Request, Response}; 33 use hyper::error::UriError; 34 use hyper::header::{Authorization, Basic, ContentLength, ContentType}; 35 use hyper::Error as HyperError; 36 use hyper::Method; 37 use hyper::StatusCode; 38 use hyper::Uri; 39 use protobuf; 40 use protobuf::Message; 41 use tokio_core::reactor::Handle; 42 43 use sawtooth_sdk::messages::batch::BatchList; 44 45 use batch_submit::BatchListResult; 46 use batch_submit::BatchReadingError; 47 48 use batch_map::BatchMap; 49 50 #[derive(Debug)] 51 pub enum WorkloadError { 52 HttpError(HyperError), 53 UriError(UriError), 54 ProtobufError(protobuf::ProtobufError), 55 BatchReadingError(BatchReadingError), 56 IoError(io::Error), 57 NoBatchError, 58 UnknownRestApiError, 59 } 60 61 impl fmt::Display for WorkloadError { 62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 63 match *self { 64 WorkloadError::HttpError(ref err) => write!(f, "An http error occurred: {}", err), 65 WorkloadError::UriError(ref err) => write!(f, "A uri error occurred: {}", err), 66 WorkloadError::IoError(ref err) => write!(f, "An io error occurred: {}", err), 67 WorkloadError::ProtobufError(ref err) => { 68 write!(f, "A protobuf error occurred: {}", err) 69 } 70 WorkloadError::BatchReadingError(ref err) => { 71 write!(f, "A generic batch reading error occurred: {}", err) 72 } 73 WorkloadError::NoBatchError => write!( 74 f, 75 "An unknown error occurred resulting in a lack of a batch to send" 76 ), 77 WorkloadError::UnknownRestApiError => write!( 78 f, 79 "This error produced a rest api error that should be handled." 80 ), 81 } 82 } 83 } 84 85 impl error::Error for WorkloadError { 86 fn cause(&self) -> Option<&error::Error> { 87 match *self { 88 WorkloadError::HttpError(ref err) => err.cause(), 89 WorkloadError::UriError(ref err) => err.cause(), 90 WorkloadError::IoError(ref err) => err.cause(), 91 WorkloadError::ProtobufError(ref err) => err.cause(), 92 WorkloadError::BatchReadingError(ref err) => err.cause(), 93 WorkloadError::NoBatchError => Some(&WorkloadError::NoBatchError), 94 WorkloadError::UnknownRestApiError => Some(&WorkloadError::UnknownRestApiError), 95 } 96 } 97 98 fn description(&self) -> &str { 99 match *self { 100 WorkloadError::HttpError(ref err) => err.description(), 101 WorkloadError::UriError(ref err) => err.description(), 102 WorkloadError::IoError(ref err) => err.description(), 103 WorkloadError::ProtobufError(ref err) => err.description(), 104 WorkloadError::BatchReadingError(ref err) => err.description(), 105 WorkloadError::NoBatchError => { 106 "There was an error resulting in lacking a batch to submit." 107 } 108 WorkloadError::UnknownRestApiError => { 109 "The rest api produced an error response that we were not expecting." 110 } 111 } 112 } 113 } 114 115 impl From<BatchReadingError> for WorkloadError { 116 fn from(err: BatchReadingError) -> Self { 117 WorkloadError::BatchReadingError(err) 118 } 119 } 120 121 impl From<io::Error> for WorkloadError { 122 fn from(err: io::Error) -> Self { 123 WorkloadError::IoError(err) 124 } 125 } 126 127 impl From<protobuf::ProtobufError> for WorkloadError { 128 fn from(err: protobuf::ProtobufError) -> Self { 129 WorkloadError::ProtobufError(err) 130 } 131 } 132 133 impl From<HyperError> for WorkloadError { 134 fn from(err: HyperError) -> Self { 135 WorkloadError::HttpError(err) 136 } 137 } 138 139 impl From<UriError> for WorkloadError { 140 fn from(err: UriError) -> Self { 141 WorkloadError::UriError(err) 142 } 143 } 144 145 /// Counts sent, committed, invalid, and queue full for Batches and Batch Status responses 146 // from the Sawtooth REST Api. 147 pub struct HTTPRequestCounter { 148 sent_count: AtomicUsize, 149 queue_full_count: AtomicUsize, 150 } 151 152 impl HTTPRequestCounter { 153 pub fn new() -> Self { 154 HTTPRequestCounter { 155 sent_count: AtomicUsize::new(0), 156 queue_full_count: AtomicUsize::new(0), 157 } 158 } 159 160 pub fn increment_sent(&self) { 161 self.sent_count.fetch_add(1, Ordering::Relaxed); 162 } 163 164 pub fn increment_queue_full(&self) { 165 self.queue_full_count.fetch_add(1, Ordering::Relaxed); 166 } 167 168 pub fn log(&self, seconds: u64, nanoseconds: u32) { 169 let update = seconds as f64 + f64::from(nanoseconds) * 1e-9; 170 println!( 171 "{}, Batches/s {:.3}", 172 self, 173 self.sent_count.load(Ordering::Relaxed) as f64 / update 174 ); 175 176 self.sent_count.store(0, Ordering::Relaxed); 177 self.queue_full_count.store(0, Ordering::Relaxed); 178 } 179 } 180 181 impl fmt::Display for HTTPRequestCounter { 182 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 183 let time = chrono::Utc::now(); 184 write!( 185 f, 186 "{0}, Sent: {1}, Queue Full {2}", 187 time.format("%h-%d-%Y %H:%M:%S%.3f").to_string(), 188 self.sent_count.load(Ordering::Relaxed), 189 self.queue_full_count.load(Ordering::Relaxed) 190 ) 191 } 192 } 193 194 /// Log if time since last log is greater than update time. 195 pub fn log( 196 counter: &Rc<HTTPRequestCounter>, 197 last_log_time: &mut time::Instant, 198 update_time: u32, 199 ) -> Result<(), WorkloadError> { 200 let log_time = time::Instant::now() - *last_log_time; 201 if log_time.as_secs() as u32 >= update_time { 202 counter.log(log_time.as_secs(), log_time.subsec_nanos()); 203 *last_log_time = time::Instant::now(); 204 } 205 Ok(()) 206 } 207 208 /// Call next on the BatchList Iterator and return the batchlist if no error. 209 pub fn get_next_batchlist( 210 batch_list_iter: &mut Iterator<Item = BatchListResult>, 211 batch_map: &Rc<RefCell<BatchMap>>, 212 batches: &Rc<RefCell<Vec<BatchList>>>, 213 ) -> Result<BatchList, WorkloadError> { 214 match batches.borrow_mut().pop() { 215 Some(batchlist) => Ok(batchlist), 216 None => match batch_list_iter.next() { 217 Some(Ok(batch_list)) => { 218 batch_map.borrow_mut().add(batch_list.clone()); 219 Ok(batch_list) 220 } 221 Some(Err(err)) => Err(WorkloadError::from(err)), 222 None => Err(WorkloadError::NoBatchError), 223 }, 224 } 225 } 226 227 /// Create the request from the next Target Url and the batchlist. 228 pub fn form_request_from_batchlist( 229 targets: &mut Cycle<IntoIter<String>>, 230 batch_list: Result<BatchList, WorkloadError>, 231 basic_auth: &Option<String>, 232 ) -> Result<(Request, Option<String>), WorkloadError> { 233 let mut batch_url = targets.next().unwrap(); 234 batch_url.push_str("/batches"); 235 debug!("Batches POST: {}", batch_url); 236 237 let batchlist_unwrapped = batch_list?; 238 239 let batch_id = match batchlist_unwrapped.batches.last() { 240 Some(batch) => Some(batch.header_signature.clone()), 241 None => None, 242 }; 243 let bytes = batchlist_unwrapped.write_to_bytes()?; 244 let mut req = Request::new(Method::Post, Uri::from_str(&batch_url)?); 245 let content_len = bytes.len() as u64; 246 req.set_body(bytes); 247 req.headers_mut().set(ContentType::octet_stream()); 248 req.headers_mut().set(ContentLength(content_len)); 249 250 if let Some(ref basic_auth) = *basic_auth { 251 req.headers_mut() 252 .set(Authorization(Basic::from_str(&basic_auth)?)); 253 } 254 255 Ok((req, batch_id)) 256 } 257 258 /// Log if there is a HTTP Error. 259 fn handle_http_error( 260 response: Result<Response, HyperError>, 261 batch_id: Option<String>, 262 batches: &Rc<RefCell<Vec<BatchList>>>, 263 batch_map: &Rc<RefCell<BatchMap>>, 264 counter: &Rc<HTTPRequestCounter>, 265 ) -> Result<(), HyperError> { 266 if let Some(batch_id) = batch_id { 267 match response { 268 Ok(response) => match response.status() { 269 StatusCode::Accepted => batch_map.borrow_mut().mark_submit_success(&batch_id), 270 StatusCode::TooManyRequests => counter.increment_queue_full(), 271 272 _ => if let Some(batchlist) = 273 batch_map.borrow_mut().get_batchlist_to_submit(&batch_id) 274 { 275 batches.borrow_mut().push(batchlist) 276 }, 277 }, 278 Err(err) => { 279 if let Some(batchlist) = batch_map.borrow_mut().get_batchlist_to_submit(&batch_id) { 280 batches.borrow_mut().push(batchlist) 281 } 282 info!("{}", err); 283 } 284 } 285 } 286 Ok(()) 287 } 288 289 /// POST the batchlist to the rest api. 290 pub fn make_request( 291 client: &Rc<Client<HttpConnector>>, 292 handle: &Handle, 293 counter: Rc<HTTPRequestCounter>, 294 batch_map: Rc<RefCell<BatchMap>>, 295 batches: Rc<RefCell<Vec<BatchList>>>, 296 req: Result<(Request, Option<String>), WorkloadError>, 297 ) -> Result<(), WorkloadError> { 298 let handle_clone = handle.clone(); 299 match req { 300 Ok((req, batch_id)) => { 301 counter.increment_sent(); 302 let response_future = client 303 .request(req) 304 .then(move |response: Result<Response, HyperError>| { 305 handle_http_error(response, batch_id, &batches, &batch_map, &counter) 306 }) 307 .map(|_| ()) 308 .map_err(|_| ()); 309 310 handle_clone.spawn(response_future); 311 312 Ok(()) 313 } 314 315 Err(err) => Err(err), 316 } 317 }