Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge changes from 3.5.2 into series/3.x #3858

Merged
merged 19 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions .github/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ changelog:
categories:
- title: Features
labels:
- :mushroom: enhancement
- ':mushroom: enhancement'
- title: Bug Fixes
labels:
- :beetle: bug
- ':beetle: bug'
- title: Behind the Scenes
labels:
- :gear: infrastructure
- :robot:
- ':gear: infrastructure'
- ':robot:'
- title: Documentation
labels:
- :books: docs
- ':books: docs'
# Not for published notes, just to make sure we don't forget any accidentally unlabeled PRs
- title: Uncategorized
labels:
- '*'
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@

## Getting Started

- Wired: **3.5.1**
- Wired: **3.5.2**
- Tired: **2.5.5** (end of life)

```scala
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.1"
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.2"
```

The above represents the core, stable dependency which brings in the entirety of Cats Effect. This is *most likely* what you want. All current Cats Effect releases are published for Scala 2.12, 2.13, 3.0, and Scala.js 1.7.
Expand All @@ -30,22 +30,22 @@ Depending on your use-case, you may want to consider one of the several other mo

```scala
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect-kernel" % "3.5.1",
"org.typelevel" %% "cats-effect-laws" % "3.5.1" % Test)
"org.typelevel" %% "cats-effect-kernel" % "3.5.2",
"org.typelevel" %% "cats-effect-laws" % "3.5.2" % Test)
```

If you're a middleware framework (like [Fs2](https://fs2.io/)), you probably want to depend on **std**, which gives you access to `Queue`, `Semaphore`, and much more without introducing a hard-dependency on `IO` outside of your tests:

```scala
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-effect-std" % "3.5.1",
"org.typelevel" %% "cats-effect" % "3.5.1" % Test)
"org.typelevel" %% "cats-effect-std" % "3.5.2",
"org.typelevel" %% "cats-effect" % "3.5.2" % Test)
```

You may also find some utility in the **testkit** and **kernel-testkit** projects, which contain `TestContext`, generators for `IO`, and a few other things:

```scala
libraryDependencies += "org.typelevel" %% "cats-effect-testkit" % "3.5.1" % Test
libraryDependencies += "org.typelevel" %% "cats-effect-testkit" % "3.5.2" % Test
```

Cats Effect provides backward binary compatibility within the 2.x and 3.x version lines, and both forward and backward compatibility within any major/minor line. This is analogous to the versioning scheme used by Cats itself, as well as other major projects such as Scala.js. Thus, any project depending upon Cats Effect 2.2.1 can be used with libraries compiled against Cats Effect 2.0.0 or 2.2.3, but *not* with libraries compiled against 2.3.0 or higher.
Expand Down
2 changes: 1 addition & 1 deletion docs/core/native-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ThisBuild / scalaVersion := "2.13.8"

lazy val root = (project in file(".")).enablePlugins(NativeImagePlugin).settings(
name := "cats-effect-3-hello-world",
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.1",
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.2",
Compile / mainClass := Some("com.example.Main"),
nativeImageOptions += "--no-fallback",
nativeImageVersion := "22.1.0" // It should be at least version 21.0.0
Expand Down
2 changes: 1 addition & 1 deletion docs/core/scala-native.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ lazy val root = project.in(file("."))
.enablePlugins(ScalaNativePlugin)
.settings(
name := "cats-effect-3-hello-world",
libraryDependencies += "org.typelevel" %%% "cats-effect" % "3.5.1",
libraryDependencies += "org.typelevel" %%% "cats-effect" % "3.5.2",
Compile / mainClass := Some("com.example.Main")
)

Expand Down
2 changes: 1 addition & 1 deletion docs/core/test-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ For those migrating code from Cats Effect 2, `TestControl` is a considerably mor
In order to use `TestControl`, you will need to bring in the **cats-effect-testkit** dependency:

```scala
libraryDependencies += "org.typelevel" %% "cats-effect-testkit" % "3.5.1" % Test
libraryDependencies += "org.typelevel" %% "cats-effect-testkit" % "3.5.2" % Test
```

## Example
Expand Down
2 changes: 1 addition & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ title: FAQ

```scala-cli
//> using scala "2.13.8"
//> using lib "org.typelevel::cats-effect::3.4.11"
//> using lib "org.typelevel::cats-effect::3.5.2"

import cats.effect._

Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: Getting Started
Add the following to your **build.sbt**:

```scala
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.1"
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.2"
```

Naturally, if you're using ScalaJS, you should replace the double `%%` with a triple `%%%`. If you're on Scala 2, it is *highly* recommended that you enable the [better-monadic-for](https://github.com/oleg-py/better-monadic-for) plugin, which fixes a number of surprising elements of the `for`-comprehension syntax in the Scala language:
Expand Down Expand Up @@ -62,7 +62,7 @@ We will learn more about constructs like `start` and `*>` in later pages, but fo
Of course, the easiest way to play with Cats Effect is to try it out in a Scala REPL. We recommend using [Ammonite](https://ammonite.io/#Ammonite-REPL) for this kind of thing. To get started, run the following lines (if not using Ammonite, skip the first line and make sure that Cats Effect and its dependencies are correctly configured on the classpath):

```scala
import $ivy.`org.typelevel::cats-effect:3.4.11`
import $ivy.`org.typelevel::cats-effect:3.5.2`

import cats.effect.unsafe.implicits._
import cats.effect.IO
Expand Down
8 changes: 4 additions & 4 deletions docs/migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ Cats Effect 3 splits the code dependency into multiple modules. If you were prev
The current non-test modules are:

```scala
"org.typelevel" %% "cats-effect-kernel" % "3.5.1",
"org.typelevel" %% "cats-effect-std" % "3.5.1",
"org.typelevel" %% "cats-effect" % "3.5.1",
"org.typelevel" %% "cats-effect-kernel" % "3.5.2",
"org.typelevel" %% "cats-effect-std" % "3.5.2",
"org.typelevel" %% "cats-effect" % "3.5.2",
```

- `kernel` - type class definitions, simple concurrency primitives
Expand All @@ -96,7 +96,7 @@ The current non-test modules are:
libraryDependencies ++= Seq(
//...
- "org.typelevel" %% "cats-effect" % "2.4.0",
+ "org.typelevel" %% "cats-effect" % "3.5.1",
+ "org.typelevel" %% "cats-effect" % "3.5.2",
//...
)
```
Expand Down
59 changes: 59 additions & 0 deletions docs/std/mapref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
id: mapref
title: MapRef
---

A total map from a key to a `Ref` of its value.

```scala mdoc:silent
import cats.effect.Ref

trait MapRef[F[_], K, V] {

/**
* Access the reference for this Key
*/
def apply(k: K): Ref[F, V]
}
```

It is conceptually similar to a `Ref[F, Map[K, V]]`,
but with better ergonomics when working on a per key basis.
Note, however, that it does not support atomic updates to multiple keys.

Additionally, some implementations also provide less contention:
since all operations are performed on individual key-value pairs,
the pairs can be sharded by key.
Thus, multiple concurrent updates may be executed independently to each other,
as long as their keys belong to different shards.

### In-Memory database

This is probably one of the most common uses of this datatype.

```scala mdoc:reset:silent
//> using lib "org.typelevel::cats-effect::3.5.2"

import cats.effect.IO
import cats.effect.std.MapRef

trait DatabaseClient[F[_], Id, Data] {
def getDataById(id: Id): F[Option[Data]]
def upsertData(id: Id, data: Data): F[Unit]
}

object DatabaseClient {
def inMemory[Id, Data]: IO[DatabaseClient[IO, Id, Data]] =
MapRef.ofShardedImmutableMap[IO, Id, Data](
shardCount = 5 // Arbitrary number of shards just for demonstration.
).map { mapRef =>
new DatabaseClient[IO, Id, Data] {
override def getDataById(id: Id): IO[Option[Data]] =
mapRef(id).get

override def upsertData(id: Id, data: Data): IO[Unit] =
mapRef(id).update(_ => Some(data))
}
}
}
```
2 changes: 1 addition & 1 deletion docs/std/ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This is probably one of the most common uses of this concurrency primitive.
In this example, the workers will concurrently run and update the value of the `Ref`.

```scala mdoc:reset:silent
//> using lib "org.typelevel::cats-effect:3.4.11"
//> using lib "org.typelevel::cats-effect::3.5.2"

import cats.effect.{IO, IOApp, Sync}
import cats.effect.kernel.Ref
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ running the code snippets in this tutorial, it is recommended to use the same
```scala
name := "cats-effect-tutorial"

version := "3.5.1"
version := "3.5.2"

scalaVersion := "2.13.6"

libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.1" withSources() withJavadoc()
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.5.2" withSources() withJavadoc()

scalacOptions ++= Seq(
"-feature",
Expand Down
6 changes: 5 additions & 1 deletion kernel/shared/src/main/scala/cats/effect/kernel/Ref.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ import cats.syntax.all._
* mutable data as `AtomicReference#compareAndSet` and friends are dependent upon object
* reference equality.
*
* See also `cats.effect.std.AtomicCell` class from `cats-effect-std` for an alternative.
* See also `cats.effect.std.AtomicCell` class from `cats-effect-std` for an alternative that
* ensures exclusive access and effectual updates.
*
* If your contents are an immutable `Map[K, V]`, and all your operations are per-key, consider
* using `cats.effect.std.MapRef`.
*/
abstract class Ref[F[_], A] extends RefSource[F, A] with RefSink[F, A] {

Expand Down
2 changes: 1 addition & 1 deletion std/shared/src/main/scala/cats/effect/std/Hotswap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ object Hotswap {

def finalize(state: Ref[F, State]): F[Unit] =
state.getAndSet(Finalized).flatMap {
case Acquired(_, finalizer) => finalizer
case Acquired(_, finalizer) => exclusive.surround(finalizer)
case Cleared => F.unit
case Finalized => raise("Hotswap already finalized")
}
Expand Down
11 changes: 9 additions & 2 deletions std/shared/src/main/scala/cats/effect/std/MapRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean

/**
* This is a total map from K to Ref[F, V]. This allows us to use the Ref API backed by a
* ConcurrentHashMap or similar.
* This is a total map from `K` to `Ref[F, V]`.
*
* It is conceptually similar to a `Ref[F, Map[K, V]]`, but with better ergonomics when working
* on a per key basis. Note, however, that it does not support atomic updates to multiple keys.
*
* Additionally, some implementations also provide less contention: since all operations are
* performed on individual key-value pairs, the pairs can be sharded by key. Thus, multiple
* concurrent updates may be executed independently to each other, as long as their keys belong
* to different shards.
*/
trait MapRef[F[_], K, V] extends Function1[K, Ref[F, V]] {

Expand Down
17 changes: 17 additions & 0 deletions tests/shared/src/test/scala/cats/effect/std/HotswapSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,23 @@ class HotswapSpec extends BaseSpec { outer =>
go must completeAs(())
}

"not finalize Hotswap while resource is in use" in ticked { implicit ticker =>
val r = Resource.make(IO.ref(true))(_.set(false))
val go = Hotswap.create[IO, Ref[IO, Boolean]].allocated.flatMap {
case (hs, fin) =>
hs.swap(r) *> (IO.sleep(1.second) *> fin).background.surround {
hs.get.use {
case Some(ref) =>
val notReleased = ref.get.flatMap(b => IO(b must beTrue))
notReleased *> IO.sleep(2.seconds) *> notReleased.void
case None => IO(false must beTrue).void
}
}
}

go must completeAs(())
}

"resource can be accessed concurrently" in ticked { implicit ticker =>
val go = Hotswap.create[IO, Unit].use { hs =>
hs.swap(Resource.unit) *>
Expand Down
Loading