Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
adamw committed Oct 23, 2024
1 parent b19d1e3 commit 39d63d2
Show file tree
Hide file tree
Showing 27 changed files with 309 additions and 162 deletions.
2 changes: 1 addition & 1 deletion generated-doc/out/adr/0002-retries.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Date: 2023-11-30

## Context

How should the [retries API](../retries.md) be implemented in terms of:
How should the [retries API](../utils/retries.md) be implemented in terms of:
- developer friendliness,
- supported ways of representing the operation under retry,
- possibly infinite retries.
Expand Down
4 changes: 2 additions & 2 deletions generated-doc/out/adr/0008-scheduled-repeat-retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ Date: 2024-07-09

## Context

How should the [retries](../retries.md) and [repeat](../repeat.md) APIs have the common implementation.
How should the [retries](../utils/retries.md) and [repeat](../utils/repeat.md) APIs have the common implementation.

## Decision

We're introducing [scheduled](../scheduled.md) as a common API for both retries and repeats.
We're introducing [scheduled](../utils/scheduled.md) as a common API for both retries and repeats.

In addition, `Schedule` trait and its implementations are decoupled from the retry DSL, so that they can be used for repeating as well.
`retry` API remains unchanged, but it now uses `scheduled` underneath.
Expand Down
9 changes: 6 additions & 3 deletions generated-doc/out/basics/error-handling.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ in the return type of a method, another `either:` block will be selected by the
change in execution semantics without a compile error. Consider:

```scala
import ox.either, either.*
import ox.either
import ox. either.*

def returnsEither: Either[String, Int] = ???

Expand All @@ -160,7 +161,8 @@ Now, after a small refactor of `returnsEither` return type the `returnsEither.ok
instead of short-circuiting the inner `either:` block, it would immediately jump to the outer `either:` block on errors.

```scala
import ox.either, either.*
import ox.either
import ox.either.*

def returnsEither: Either[Exception, Int] = ???

Expand All @@ -175,7 +177,8 @@ val outerResult: Either[Exception, Unit] = either:
Proper way to solve this is to extract the inner `either:` block to a separate function:

```scala
import ox.either, either.*
import ox.either
import ox.either.*

def returnsEither: Either[String, Int] = ???

Expand Down
59 changes: 0 additions & 59 deletions generated-doc/out/basics/quick-example.md

This file was deleted.

64 changes: 0 additions & 64 deletions generated-doc/out/basics/start-here.md

This file was deleted.

52 changes: 30 additions & 22 deletions generated-doc/out/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@

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

To start using Ox, add the `core` dependency as described below. Then, follow one of the topics listed in the menu
to get to know Ox's API.
To start using Ox, add the `com.softwaremill.ox::core:0.5.2` [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).

```{eval-rst}
.. include:: basics/start-here.md
:parser: markdown
```{include} tour.md
```

## Table of contents

```{eval-rst}
.. toctree::
:maxdepth: 2
:caption: Project info
info/community-support
info/dependency
info/scope
.. toctree::
:maxdepth: 2
:caption: Basics
basics/start-here
tour
basics/direct-style
basics/quick-example
basics/error-handling
.. toctree::
Expand Down Expand Up @@ -60,28 +67,29 @@ In addition to this documentation, ScalaDocs can be browsed at [https://javadoc.
:maxdepth: 2
:caption: Resiliency & utilities
oxapp
retries
repeat
scheduled
resources
control-flow
actors
utility
utils/oxapp
utils/retries
utils/repeat
utils/scheduled
utils/resources
utils/control-flow
utils/actors
utils/utility
.. toctree::
:maxdepth: 2
:caption: Integrations
kafka
mdc-logback
integrations/kafka
integrations/mdc-logback
.. toctree::
:maxdepth: 2
:caption: Other topics
dictionary
best-practices
performance
compare-gears
compare-funeff
other/links
other/dictionary
other/best-practices
other/performance
other/compare-gears
other/compare-funeff
19 changes: 19 additions & 0 deletions generated-doc/out/info/community-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Community & support

## Community

If you'd have feedback, development ideas or critique, please head to our [community forum](https://softwaremill.community/c/ox/12)!
Alternatively, you can create an issue or submit a pull request on [GitHub](https://github.com/softwaremill/ox).

## Sponsors

Development and maintenance of Ox is sponsored by [SoftwareMill](https://softwaremill.com), a software development and consulting company.
We help clients scale their business through software. Our areas of expertise include backends, distributed systems,
machine learning and data analytics.

[![](https://files.softwaremill.com/logo/logo.png "SoftwareMill")](https://softwaremill.com)

## Commercial Support

We offer commercial support for Ox and related technologies, as well as development services.
[Contact us](https://softwaremill.com/contact/) to learn more about our offer!
15 changes: 15 additions & 0 deletions generated-doc/out/info/dependency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Dependency (sbt, scala-cli, etc.)

To use ox core in your project, add:

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

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

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.

Integration modules have separate dependencies.
24 changes: 24 additions & 0 deletions generated-doc/out/info/scope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Project scope

The areas that we'd like to cover with Ox are:

* concurrency: developer-friendly structured concurrency, high-level concurrency operators, safe low-level primitives,
communication between concurrently running computations
* error management: retries, timeouts, a safe approach to error propagation, safe resource management
* scheduling & timers
* resiliency: circuit breakers, bulkheads, rate limiters, backpressure

All of the above should allow for observability of the orchestrated business logic. We aim to enable writing simple,
expression-oriented code in functional style. We'd like to keep the syntax overhead to a minimum, preserving
developer-friendly stack traces, and without compromising performance.

Some of the above are already addressed in the API, some are coming up in the future. We'd love your help in shaping the
project!

## Inspiration & building blocks

* [Project Loom](https://openjdk.org/projects/loom/) (virtual threads)
* structured concurrency Java APIs ([JEP 428](https://openjdk.org/jeps/428))
* scoped values ([JEP 429](https://openjdk.org/jeps/429))
* fast, scalable [Go](https://golang.org)-like channels using [jox](https://github.com/softwaremill/jox)
* the [Scala 3](https://www.scala-lang.org) programming language
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The best place would be the application's entrypoint (the `main` method).
Once this is done, inheritable MDC values can be set in a scoped & structured manner using `InheritableMDC.supervisedWhere`
and variants.

As inheritable MDC values use a [`ForkLocal`](structured-concurrency/fork-local.md) under the hood, their usage
As inheritable MDC values use a [`ForkLocal`](../structured-concurrency/fork-local.md) under the hood, their usage
restrictions apply: outer concurrency scopes should not be used to create forks within the scopes. Only newly created
scopes, or the provided scope can be used to create forks. That's why `supervisedError`, `unsupervisedError` and
`supervisedErrorWhere` methods are provided.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ practices which might be useful for anybody starting their journey with direct-s
If you end up using concurrency scopes such as `supervised`, make sure that their lifetime is as short as possible. In
some cases it might be necessary to start a "global" scope (e.g. for application-wide, long-running tasks), but even
if so, don't let the global scope leak to any other parts of your code, and isolate its usage, e.g. using
[actors](actors.md).
[actors](../utils/actors.md).

For all other tasks, create short-lived scopes, which handle a single request, message from a queue or a single job
instance.

## Integrate with callback-based APIs using channels

Callback-based APIs, including "reactive" ones, are by their nature non-structured, and don't play well with
structured concurrency. For such cases, [channels](streaming/channels.md) are an ideal tool. Sending or receiving to/from
structured concurrency. For such cases, [channels](../streaming/channels.md) are an ideal tool. Sending or receiving to/from
a channel doesn't require any context, and can be done from any thread. On the other hand, processing the data that
is on the channel often involves concurrency and creating thread, which can be then done in a structured way.

Expand All @@ -36,7 +36,7 @@ Transforming channels directly might lead to excessive concurrency, as each tran
background fork, processing the data and sending it to a new channel. While this still performs well, as creating
virtual threads & channels is cheap, it might incur an unnecessary overhead.

Instead, you can use [flows](streaming/flows.md) and their high-level API, which allows inserting asynchronous
Instead, you can use [flows](../streaming/flows.md) and their high-level API, which allows inserting asynchronous
boundaries when necessary, but otherwise runs the subsequent processing stages on the same thread.

## Avoid returning `Fork`
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ Originally [posted on Reddit](https://www.reddit.com/r/scala/comments/1cdfaki/co
5. The programming styles that both libraries offer are slightly different as well. In Gears, most of the provided functionalities operate on the level of Gears-`Future`s. While in Ox, you often operate on thunks `=> T` or `() => T`. The Gears approach is more general, however the Ox one is more "direct" for the common case.
6. That might be considered a small detail, but we think naming is important. Gears is centered around a `Future` abstraction, which is a distinct type from `scala.concurrent.Future`. This might create unnecessary confusion, plus in our opinion it's worthy to distinguish between [promise-like futures and thread-like futures](https://softwaremill.com/two-types-of-futures/). That's why in Ox we've got `Fork`s instead (which are a thread-like-future data type). However, you don't use that type often, because of (5) above.
7. Another rather fundamental difference is in our approach to error handling. In Ox, when you create a `supervised` scope with `fork`s inside, if any of the fork fails, the whole scope fails and re-throws this exception (a variant of let-it-crash). In Gears, the default is to have failed futures, and the errors are only discovered when `.join`ing them. Both approaches have their merits, however the Ox one, where you have to explicitly create unsupervised forks (using `forkUnsupervised`), seems safer: your code might crash, but you won't miss an error.
8. Speaking of error handling, Ox provides support for various `ErrorMode`s, that is situations where you want to represent errors-as-values (in addition to exceptions). We [propose a specific way](basics/error-handling.md) to represent such application/logical errors (using `Either`s), with the built-in concurrency operators often having `Either`-variants, and by providing a boundary-break implementation for `Either`s. Gears might be getting something similar, so this might stop being an actual difference.
8. Speaking of error handling, Ox provides support for various `ErrorMode`s, that is situations where you want to represent errors-as-values (in addition to exceptions). We [propose a specific way](../basics/error-handling.md) to represent such application/logical errors (using `Either`s), with the built-in concurrency operators often having `Either`-variants, and by providing a boundary-break implementation for `Either`s. Gears might be getting something similar, so this might stop being an actual difference.
9. In Gears, there are two capabilities: `Async` and `Async.Spawn`. The first one represents a capability to suspend, the second - to fork. In Ox, we only have the `Ox` capability, which corresponds to `Async.Spawn`. You don't need a capability to suspend. This might be seen as a feature, or a bug. On one hand, it might be useful to know that somewhere down the call chain, your code will want to suspend, and more importantly, to be interrupted. That's the kind of information you get with `Async` in Gears. On the other hand, our worry is that `using Async` will be the new `implicit ec: ExecutionContext`. We're not ruling out adding an `Async`-like capability to Ox, it might turn out to be the right thing to do, but we'd still like to explore some other options (as hinted in the [Scalar talk](https://www.youtube.com/watch?v=C3j4AsFcxmM), near the end)
10. Ox's Kotlin-inspired `Channel` & `select` [implementation](https://github.com/softwaremill/jox) is less flexible than the one in Gears (you can't nest select's that easily), however it might be more performant. Initial benchmarks of a WebSocket server using Java 21 & Ox streaming turned out better or matching the asynchronous implementations.
File renamed without changes.
15 changes: 15 additions & 0 deletions generated-doc/out/other/links.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Blogs, videos, ...

## Blogs

* [Prototype Loom-based concurrency API for Scala](https://softwaremill.com/prototype-loom-based-concurrency-api-for-scala/)
* [Go-like channels using project Loom and Scala](https://softwaremill.com/go-like-channels-using-project-loom-and-scala/)
* [Two types of futures](https://softwaremill.com/two-types-of-futures/)
* [Supervision, Kafka and Java 21: what’s new in Ox](https://softwaremill.com/supervision-kafka-and-java-21-whats-new-in-ox/)
* [Designing a (yet another) retry API](https://softwaremill.com/designing-a-yet-another-retry-api/)
* [Handling errors in direct-style Scala](https://softwaremill.com/handling-errors-in-direct-style-scala/)
* [Direct-style concurrent streaming](https://softwaremill.com/direct-style-concurrent-streaming/)

## Videos

Coming up!
File renamed without changes.
4 changes: 4 additions & 0 deletions generated-doc/out/streaming/flows.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ A `Flow[T]` describes an asynchronous data transformation pipeline. When run, it

Flows are lazy, evaluation (and any effects) happen only when the flow is run. Flows might be finite or infinite; in the latter case running a flow never ends normally; it might be interrupted, though. Finally, any exceptions that occur when evaluating the flow's logic will be thrown when running the flow, after any cleanup logic completes.

```{note}
An introduction to Ox's Flow, along with some code samples is available [as a video](https://www.youtube.com/watch?v=2sZGVRXP9PM).
```

## Creating flows

There's a number of methods on the `Flow` companion object that can be used to create a flow:
Expand Down
Loading

0 comments on commit 39d63d2

Please sign in to comment.