docs: Bucket getCurrentBucket() {
          long currentTime = time.getCurrentTimeInMillis();
  
          /* a shortcut to try and get the most common result of immediately finding the current bucket */
  
          /**
           * Retrieve the latest bucket if the given time is BEFORE the end of the bucket window, otherwise it returns NULL.
           * 
           * NOTE: This is thread-safe because it's accessing 'buckets' which is a LinkedBlockingDeque
           */
          Bucket currentBucket = buckets.peekLast();
          if (currentBucket != null && currentTime < currentBucket.windowStart + this.bucketSizeInMillseconds) {
              // if we're within the bucket 'window of time' return the current one
              // NOTE: We do not worry if we are BEFORE the window in a weird case of where thread scheduling causes that to occur,
              // we'll just use the latest as long as we're not AFTER the window
              return currentBucket;
          }
  
          /* if we didn't find the current bucket above, then we have to create one */
  
          /**
           * The following needs to be synchronized/locked even with a synchronized/thread-safe data structure such as LinkedBlockingDeque because
           * the logic involves multiple steps to check existence, create an object then insert the object. The 'check' or 'insertion' themselves
           * are thread-safe by themselves but not the aggregate algorithm, thus we put this entire block of logic inside synchronized.
           * 
           * I am using a tryLock if/then (http://download.oracle.com/javase/6/docs/api/java/util/concurrent/locks/Lock.html#tryLock())
           * so that a single thread will get the lock and as soon as one thread gets the lock all others will go the 'else' block
           * and just return the currentBucket until the newBucket is created. This should allow the throughput to be far higher
           * and only slow down 1 thread instead of blocking all of them in each cycle of creating a new bucket based on some testing
           * (and it makes sense that it should as well).
           * 
           * This means the timing won't be exact to the millisecond as to what data ends up in a bucket, but that's acceptable.
           * It's not critical to have exact precision to the millisecond, as long as it's rolling, if we can instead reduce the impact synchronization.
           * 
           * More importantly though it means that the 'if' block within the lock needs to be careful about what it changes that can still
           * be accessed concurrently in the 'else' block since we're not completely synchronizing access.
           * 
           * For example, we can't have a multi-step process to add a bucket, remove a bucket, then update the sum since the 'else' block of code
           * can retrieve the sum while this is all happening. The trade-off is that we don't maintain the rolling sum and let readers just iterate
           * bucket to calculate the sum themselves. This is an example of favoring write-performance instead of read-performance and how the tryLock
           * versus a synchronized block needs to be accommodated.
           */
          if (newBucketLock.tryLock()) {
              try {
                  if (buckets.peekLast() == null) {
                      // the list is empty so create the first bucket
                      Bucket newBucket = new Bucket(currentTime);
                      buckets.addLast(newBucket);
                      return newBucket;
                  } else {
                      // We go into a loop so that it will create as many buckets as needed to catch up to the current time
                      // as we want the buckets complete even if we don't have transactions during a period of time.
                      for (int i = 0; i < numberOfBuckets; i++) {
                          // we have at least 1 bucket so retrieve it
                          Bucket lastBucket = buckets.peekLast();
                          if (currentTime < lastBucket.windowStart + this.bucketSizeInMillseconds) {
                              // if we're within the bucket 'window of time' return the current one
                              // NOTE: We do not worry if we are BEFORE the window in a weird case of where thread scheduling causes that to occur,
                              // we'll just use the latest as long as we're not AFTER the window
                              return lastBucket;
                          } else if (currentTime - (lastBucket.windowStart + this.bucketSizeInMillseconds) > timeInMilliseconds) {
                              // the time passed is greater than the entire rolling counter so we want to clear it all and start from scratch
                              reset();
                              // recursively call getCurrentBucket which will create a new bucket and return it
                              return getCurrentBucket();
                          } else { // we're past the window so we need to create a new bucket
                              // create a new bucket and add it as the new 'last'
                              buckets.addLast(new Bucket(lastBucket.windowStart + this.bucketSizeInMillseconds));
                              // add the lastBucket values to the cumulativeSum
                              cumulativeSum.addBucket(lastBucket);
                          }
                      }
                      // we have finished the for-loop and created all of the buckets, so return the lastBucket now
                      return buckets.peekLast();
                  }
              } finally {
                  newBucketLock.unlock();
              }
          } else {
              currentBucket = buckets.peekLast();
              if (currentBucket != null) {
                  // we didn't get the lock so just return the latest bucket while another thread creates the next one
                  return currentBucket;
              } else {
                  // the rare scenario where multiple threads raced to create the very first bucket
                  // wait slightly and then use recursion while the other thread finishes creating a bucket
                  try {
                      Thread.sleep(5);
                  } catch (Exception e) {
                      // ignore
                  }
                  return getCurrentBucket();
              }
          }
      }