Giter Club home page Giter Club logo

Comments (2)

jxtps avatar jxtps commented on July 24, 2024

Ended up rolling a fully custom solution. The TokenBucketGroup-equivalent part:

  object TokenBuckets {
    val NanosPerSecond: Long = 1000L * 1000 * 1000
  }

  class TokenBuckets(size: Int, rate: Double, minNanosPerUpdate: Long = TokenBuckets.NanosPerSecond)
                    (implicit actorSystem: ActorSystem) {

    require(size > 0)
    require(rate >= 0.0001)
    require(rate < 1e6)

    def now(): Long = System.nanoTime()

    //private val floor = 0
    private val floor = -size // Penalize abusers a little
    private val buckets = new ConcurrentHashMap[String, AtomicInteger]()
    private var lastUpdateAt: Long = now()
    private val tokensPerNano: Double = rate / TokenBuckets.NanosPerSecond
    private val nanosPerToken: Double = 1 / tokensPerNano
    private val nanosPerUpdate: Long = Math.max(nanosPerToken.ceil.toLong, minNanosPerUpdate)

    def consume(ip: String): Int = {
      var int = buckets.get(ip)
      if (int == null) {
        int = new AtomicInteger(size)
        buckets.put(ip, int) // May drop a token if there's a concurrent request, that's ok
      }
      if (int.get() > floor) int.decrementAndGet() else floor
    }

    import scala.concurrent.duration._
    import scala.concurrent.ExecutionContext.Implicits.global

    actorSystem.scheduler.schedule(nanosPerUpdate.nanos, nanosPerUpdate.nanos) {
      val deltaNanos: Long = now() - lastUpdateAt
      val tokensToAdd = (deltaNanos * tokensPerNano).round.toInt
      if (tokensToAdd > 0) { // Should always be true
        buckets.forEach((ip: String, int: AtomicInteger) => {
          val tokensInBucket = int.addAndGet(tokensToAdd)
          if (tokensInBucket >= size) buckets.remove(ip) // May drop a token if there's a concurrent request, that's ok
        })
        lastUpdateAt += (tokensToAdd * nanosPerToken).ceil.toLong
      }
    }
  }

This way there's no synchronization and only constant-time operations in the critical-path consume call, and the cleanup / adding of new tokens is done at a fixed rate in the background.

The tradeoff is that it's no longer 100% exact, so there will be an extra connection allowed through every now and then, and if the background job gets stalled for whatever reason, then there will be no new tokens granted => request stoppage.

Anyway, feel free to use or ignore, I'll close this issue in the meantime.

from play-guard.

sief avatar sief commented on July 24, 2024

sorry for the late response, I somehow didn't notice the issue. I've also been thinking about a more relaxed approach in favour of performance. But when I did some throughput measurements I was surprised that the bucket rebuild cost seems to be negligible. Did you notice any performance issues?

from play-guard.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.