Skip to content

Commit

Permalink
Release 0.5.3
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw committed Nov 15, 2024
1 parent 6bdd3d0 commit b88cd3f
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 12 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ the project!
To test Ox, use the following dependency, using either [sbt](https://www.scala-sbt.org):

```scala
"com.softwaremill.ox" %% "core" % "0.5.2"
"com.softwaremill.ox" %% "core" % "0.5.3"
```

Or [scala-cli](https://scala-cli.virtuslab.org):

```scala
//> using dep "com.softwaremill.ox::core:0.5.2"
//> using dep "com.softwaremill.ox::core:0.5.3"
```

Documentation is available at [https://ox.softwaremill.com](https://ox.softwaremill.com), ScalaDocs can be browsed at [https://javadoc.io](https://www.javadoc.io/doc/com.softwaremill.ox).
Expand Down
16 changes: 11 additions & 5 deletions generated-doc/out/basics/direct-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

## What is direct style?

Direct style is an approach to programming which leverages the built-in control flow constructs of the language as the
basic building blocks of effectful code.
Direct style is an approach to programming where the results of effectful computations are available directly, without
a "wrapper" type such as `Future`, `IO` or `Task`.

In direct style, I/O operations and thread synchronisations are executed as if they were blocking operations, that is
their result is available as the return value of the appropriate method call. However, specific direct style
implementations may contain special syntax that is needed to run such operations.
That way, direct-style programs can leverage the built-in control flow constructs of the language as the basic building
blocks of effectful code.

I/O operations and thread synchronisations are executed as if they were blocking operations, even if under the hood
they are run asynchronously, using continuations (which matter for throughput & performance).

The results of I/O operations are available "directly", as the return values of the appropriate method calls. Some
implementations may require using special syntax. In others, I/O calls are invoked as any other function or method
call, and there's no intermediate library-level runtime that is needed.

## Compiler/runtime support

Expand Down
3 changes: 2 additions & 1 deletion generated-doc/out/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Safe direct-style concurrency and resiliency for Scala on the JVM. Requires JDK 21 & Scala 3.

To start using Ox, add the `com.softwaremill.ox::core:0.5.2` [dependency](info/dependency.md) to your project.
To start using Ox, add the `com.softwaremill.ox::core:0.5.3` [dependency](info/dependency.md) to your project.
Then, take a look at the tour of Ox, or follow one of the topics listed in the menu to get to know Ox's API!

In addition to this documentation, ScalaDocs can be browsed at [https://javadoc.io](https://www.javadoc.io/doc/com.softwaremill.ox).
Expand Down Expand Up @@ -69,6 +69,7 @@ In addition to this documentation, ScalaDocs can be browsed at [https://javadoc.
utils/oxapp
utils/retries
utils/rate-limiter
utils/repeat
utils/scheduled
utils/resources
Expand Down
4 changes: 2 additions & 2 deletions generated-doc/out/info/dependency.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ To use ox core in your project, add:

```scala
// sbt dependency
"com.softwaremill.ox" %% "core" % "0.5.2"
"com.softwaremill.ox" %% "core" % "0.5.3"

// scala-cli dependency
//> using dep com.softwaremill.ox::core:0.5.2
//> using dep com.softwaremill.ox::core:0.5.3
```

Ox core depends only on the Java [jox](https://github.com/softwaremill/jox) project, where channels are implemented. There are no other direct or transitive dependencies.
Expand Down
2 changes: 1 addition & 1 deletion generated-doc/out/integrations/kafka.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Dependency:

```scala
"com.softwaremill.ox" %% "kafka" % "0.5.2"
"com.softwaremill.ox" %% "kafka" % "0.5.3"
```

`Flow`s which read from a Kafka topic, mapping stages and drains which publish to Kafka topics are available through
Expand Down
2 changes: 1 addition & 1 deletion generated-doc/out/integrations/mdc-logback.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Dependency:

```scala
"com.softwaremill.ox" %% "mdc-logback" % "0.5.2"
"com.softwaremill.ox" %% "mdc-logback" % "0.5.3"
```

Ox provides support for setting inheritable MDC (mapped diagnostic context) values, when using the [Logback](https://logback.qos.ch)
Expand Down
57 changes: 57 additions & 0 deletions generated-doc/out/utils/rate-limiter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Rate limiter

The rate limiter mechanism allows controlling the rate at which operations are executed. It ensures that at most a certain number of operations are run concurrently within a specified time frame, preventing system overload and ensuring fair resource usage. Note that the implemented limiting mechanism only takes into account the start of execution and not the whole execution of an operation.

## API

Basic rate limiter usage:

```scala
import ox.supervised
import ox.resilience.*
import scala.concurrent.duration.*

val algorithm = RateLimiterAlgorithm.FixedWindow(2, 1.second)

supervised:
val rateLimiter = RateLimiter(algorithm)

type T
def operation: T = ???

val blockedOperation: T = rateLimiter.runBlocking(operation)
val droppedOperation: Option[T] = rateLimiter.runOrDrop(operation)
```

`blockedOperation` will block the operation until the algorithm allows it to be executed. Therefore, the return type is the same as the operation. On the other hand, if the algorithm doesn't allow execution of more operations, `runOrDrop` will drop the operation returning `None` and wrapping the result in `Some` when the operation is successfully executed.

A rate limiter must be created within an `Ox` [concurrency scope](../structured-concurrency/fork-join.md), as a background fork is created, to replenish the rate limiter. Once the scope ends, the rate limiter is stops as well.

## Operation definition

The `operation` can be provided directly using a by-name parameter, i.e. `f: => T`.

## Configuration

The configuration of a `RateLimiter` depends on an underlying algorithm that controls whether an operation can be executed or not. The following algorithms are available:
- `RateLimiterAlgorithm.FixedWindow(rate: Int, dur: FiniteDuration)` - where `rate` is the maximum number of operations to be executed in fixed windows of `dur` duration.
- `RateLimiterAlgorithm.SlidingWindow(rate: Int, dur: FiniteDuration)` - where `rate` is the maximum number of operations to be executed in any window of time of duration `dur`.
- `RateLimiterAlgorithm.Bucket(maximum: Int, dur: FiniteDuration)` - where `maximum` is the maximum capacity of tokens available in the token bucket algorithm and one token is added each `dur`. It can represent both the leaky bucket algorithm or the token bucket algorithm.

### API shorthands

You can use one of the following shorthands to define a Rate Limiter with the corresponding algorithm:

- `RateLimiter.fixedWindow(rate: Int, dur: FiniteDuration)`,
- `RateLimiter.slidingWindow(rate: Int, dur: FiniteDuration)`,
- `RateLimiter.leakyBucket(maximum: Int, dur: FiniteDuration)`,

See the tests in `ox.resilience.*` for more.

## Custom rate limiter algorithms

The `RateLimiterAlgorithm` employed by `RateLimiter` can be extended to implement new algorithms or modify existing ones. Its interface is modelled like that of a `Semaphore` although the underlying implementation could be different. For best compatibility with the existing interface of `RateLimiter`, methods `acquire` and `tryAcquire` should offer the same guaranties as Java's `Semaphores`.

Additionally, there are two methods employed by the `GenericRateLimiter` for updating its internal state automatically:
- `def update(): Unit`: Updates the internal state of the rate limiter to reflect its current situation. Invoked in a background fork repeatedly, when a rate limiter is created.
- `def getNextUpdate: Long`: Returns the time in nanoseconds after which a new `update` needs to be called.

0 comments on commit b88cd3f

Please sign in to comment.