Skip to content

Commit a10a7d7

Browse files
committed
Use Ref with a single map update
compute hashes in parallel
1 parent ca97047 commit a10a7d7

File tree

4 files changed

+34
-60
lines changed

4 files changed

+34
-60
lines changed

project/src/live.server.scala

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import scala.concurrent.duration.*
33
import org.http4s.*
44
import org.http4s.HttpApp
55
import org.http4s.HttpRoutes
6-
import org.http4s.client.Client
76
import org.http4s.dsl.io.*
87
import org.http4s.ember.client.EmberClientBuilder
98
import org.http4s.ember.server.EmberServerBuilder
@@ -19,14 +18,12 @@ import fs2.concurrent.Topic
1918
import scribe.Level
2019

2120
import cats.effect.*
22-
import cats.effect.std.*
2321
import cats.implicits.*
2422

2523
import _root_.io.circe.*
2624
import _root_.io.circe.Encoder
2725

2826
import ProxyConfig.Equilibrium
29-
import scribe.Scribe
3027

3128
sealed trait FrontendEvent derives Encoder.AsObject
3229

@@ -265,27 +262,28 @@ object LiveServer
265262
.toResource
266263

267264
fileToHashRef <- Ref[IO].of(Map.empty[String, String]).toResource
268-
fileToHashMapRef = MapRef.fromSingleImmutableMapRef[IO, String, String](fileToHashRef)
269265
refreshTopic <- Topic[IO, Unit].toResource
270266
linkingTopic <- Topic[IO, Unit].toResource
271267
client <- EmberClientBuilder.default[IO].build
268+
outDirPath = fs2.io.file.Path(outDir)
269+
baseDirPath = fs2.io.file.Path(baseDir)
272270

273271
proxyRoutes: HttpRoutes[IO] <- makeProxyRoutes(client, pathPrefix, proxyConfig)(logger)
274272

275273
_ <- buildRunner(
276274
buildTool,
277275
linkingTopic,
278-
fs2.io.file.Path(baseDir),
279-
fs2.io.file.Path(outDir),
276+
baseDirPath,
277+
outDirPath,
280278
extraBuildArgs,
281279
millModuleName
282280
)(logger)
283281

284-
app <- routes(outDir.toString(), refreshTopic, indexOpts, proxyRoutes, fileToHashRef)(logger)
282+
app <- routes(outDir, refreshTopic, indexOpts, proxyRoutes, fileToHashRef)(logger)
285283

286-
_ <- seedMapOnStart(outDir, fileToHashMapRef)(logger)
284+
_ <- updateMapRef(outDirPath, fileToHashRef)(logger).toResource
287285
// _ <- stylesDir.fold(Resource.unit)(sd => seedMapOnStart(sd, mr))
288-
_ <- fileWatcher(fs2.io.file.Path(outDir), fileToHashMapRef, linkingTopic, refreshTopic)(logger)
286+
_ <- fileWatcher(outDirPath, fileToHashRef, linkingTopic, refreshTopic)(logger)
289287
// _ <- stylesDir.fold(Resource.unit[IO])(sd => fileWatcher(fs2.io.file.Path(sd), mr))
290288
_ <- logger.info(s"Start dev server on http://localhost:$port").toResource
291289
server <- buildServer(app, port)

project/src/routes.scala

Lines changed: 20 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import cats.effect.*
2727
import cats.effect.IO
2828
import cats.effect.kernel.Ref
2929
import cats.effect.kernel.Resource
30-
import cats.effect.std.*
3130
import cats.syntax.all.*
3231

3332
import _root_.io.circe.syntax.EncoderOps
@@ -147,60 +146,37 @@ def routes(
147146

148147
end routes
149148

150-
def seedMapOnStart(stringPath: String, mr: MapRef[IO, String, Option[String]])(logger: Scribe[IO]) =
151-
val asFs2 = fs2.io.file.Path(stringPath)
152-
fs2
153-
.io
154-
.file
155-
.Files[IO]
156-
.walk(asFs2)
157-
.evalMap {
158-
f =>
159-
Files[IO]
160-
.isRegularFile(f)
161-
.ifM(
162-
// logger.trace(s"hashing $f") >>
163-
fileHash(f).flatMap(
164-
h =>
165-
val key = asFs2.relativize(f)
166-
logger.trace(s"hashing $f to put at $key with hash : $h") >>
167-
mr.setKeyValue(key.toString(), h)
168-
),
169-
IO.unit
170-
)
171-
}
149+
def updateMapRef(stringPath: fs2.io.file.Path, mr: Ref[IO, Map[String, String]])(logger: Scribe[IO]) =
150+
Files[IO]
151+
.walk(stringPath)
152+
.evalFilter(Files[IO].isRegularFile)
153+
.parEvalMap(maxConcurrent = 8)(path => fileHash(path).map(path -> _))
172154
.compile
173-
.drain
174-
.toResource
175-
176-
end seedMapOnStart
155+
.toVector
156+
.flatMap(
157+
vector =>
158+
val newMap = vector
159+
.view
160+
.map(
161+
(path, hash) =>
162+
val relativizedPath = stringPath.relativize(path).toString
163+
relativizedPath -> hash
164+
)
165+
.toMap
166+
logger.trace(s"Updated hashes $newMap") *> mr.set(newMap)
167+
)
177168

178169
private def fileWatcher(
179170
stringPath: fs2.io.file.Path,
180-
mr: MapRef[IO, String, Option[String]],
171+
mr: Ref[IO, Map[String, String]],
181172
linkingTopic: Topic[IO, Unit],
182173
refreshTopic: Topic[IO, Unit]
183174
)(logger: Scribe[IO]): ResourceIO[Unit] =
184175
linkingTopic
185176
.subscribe(10)
186177
.evalTap {
187178
_ =>
188-
fs2
189-
.io
190-
.file
191-
.Files[IO]
192-
.list(stringPath)
193-
.evalTap {
194-
path =>
195-
fileHash(path).flatMap(
196-
h =>
197-
val serveAt = stringPath.relativize(fs2.io.file.Path(path.toString()))
198-
logger.trace(s"$serveAt :: hash -> $h") >>
199-
mr.setKeyValue(serveAt.toString(), h)
200-
)
201-
}
202-
.compile
203-
.drain >> refreshTopic.publish1(())
179+
updateMapRef(stringPath, mr)(logger) >> refreshTopic.publish1(())
204180
}
205181
.compile
206182
.drain

project/test/src/RoutesSpec.scala

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ class RoutesSuite extends CatsEffectSuite:
6767
for
6868
logger <- IO(scribe.cats[IO]).toResource
6969
fileToHashRef <- Ref[IO].of(Map.empty[String, String]).toResource
70-
fileToHashMapRef = MapRef.fromSingleImmutableMapRef[IO, String, String](fileToHashRef)
71-
_ <- seedMapOnStart(tempDir.toString, fileToHashMapRef)(logger)
70+
_ <- updateMapRef(tempDir.toFs2, fileToHashRef)(logger).toResource
7271
yield fileToHashRef
7372
.get
7473
.map {
@@ -86,10 +85,9 @@ class RoutesSuite extends CatsEffectSuite:
8685
fileToHashRef <- Ref[IO].of(Map.empty[String, String]).toResource
8786
linkingTopic <- Topic[IO, Unit].toResource
8887
refreshTopic <- Topic[IO, Unit].toResource
89-
fileToHashMapRef = MapRef.fromSingleImmutableMapRef[IO, String, String](fileToHashRef)
90-
_ <- fileWatcher(fs2.io.file.Path(tempDir.toString), fileToHashMapRef, linkingTopic, refreshTopic)(logger)
88+
_ <- fileWatcher(fs2.io.file.Path(tempDir.toString), fileToHashRef, linkingTopic, refreshTopic)(logger)
9189
_ <- IO.sleep(100.millis).toResource // wait for watcher to start
92-
_ <- seedMapOnStart(tempDir.toString, fileToHashMapRef)(logger)
90+
_ <- updateMapRef(tempDir.toFs2, fileToHashRef)(logger).toResource
9391
_ <- IO.blocking(os.write.over(tempDir / "test.js", "const hi = 'bye, world'")).toResource
9492
_ <- linkingTopic.publish1(()).toResource
9593
_ <- refreshTopic.subscribe(1).head.compile.resource.drain
@@ -115,8 +113,7 @@ class RoutesSuite extends CatsEffectSuite:
115113
val app = for
116114
logger <- IO(scribe.cats[IO]).toResource
117115
fileToHashRef <- Ref[IO].of(Map.empty[String, String]).toResource
118-
fileToHashMapRef = MapRef.fromSingleImmutableMapRef[IO, String, String](fileToHashRef)
119-
_ <- seedMapOnStart(tempDir.toString, fileToHashMapRef)(logger)
116+
_ <- updateMapRef(tempDir.toFs2, fileToHashRef)(logger).toResource
120117
refreshPub <- Topic[IO, Unit].toResource
121118
theseRoutes <- routes(
122119
tempDir.toString,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
extension (path: os.Path)
2+
def toFs2: fs2.io.file.Path =
3+
fs2.io.file.Path.fromNioPath(path.toNIO)

0 commit comments

Comments
 (0)