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  }