mirror of https://github.com/sbt/sbt.git
Only persist file stamps in turbo mode
The use of the persistent file stamp cache between watch runs didn't seem to cause any issues, but there was some chance for inconsistency between the file stamp cache and the file system so it makes sense to put it behind the turbo flag. After changing the default, the watch/on-change scripted test started failing. It turns out that the reason is that the file stamp cache managed by the watch process was not pre-filled by task evaluation. For this reason, the first time a source file was modified, it was treated as a creation regardless of whether or not it actually was. To fix this, I add logic to pre-fill the watch file stamp cache if we are _not_ persisting the file stamps between runs. I ran a before and after with the scala build performance benchmark tool and setting the watchPersistFileStamps key to true reduced the median run time by about 200ms in the non-turbo case.
This commit is contained in:
parent
5e374a8e7d
commit
6c4e23f77c
|
|
@ -8,7 +8,8 @@
|
|||
package sbt
|
||||
package internal
|
||||
|
||||
import java.io.{ ByteArrayInputStream, InputStream, File => _ }
|
||||
import java.io.{ ByteArrayInputStream, IOException, InputStream, File => _ }
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.atomic.{ AtomicBoolean, AtomicInteger }
|
||||
|
||||
import sbt.BasicCommandStrings.{
|
||||
|
|
@ -28,7 +29,7 @@ import sbt.internal.util.complete.{ Parser, Parsers }
|
|||
import sbt.internal.util.{ AttributeKey, JLine, Util }
|
||||
import sbt.nio.Keys.{ fileInputs, _ }
|
||||
import sbt.nio.Watch.{ Creation, Deletion, ShowOptions, Update }
|
||||
import sbt.nio.file.FileAttributes
|
||||
import sbt.nio.file.{ FileAttributes, Glob }
|
||||
import sbt.nio.{ FileStamp, FileStamper, Watch }
|
||||
import sbt.util.{ Level, _ }
|
||||
|
||||
|
|
@ -328,12 +329,14 @@ private[sbt] object Continuous extends DeprecatedContinuous {
|
|||
}
|
||||
val fileStampCache = new FileStamp.Cache
|
||||
repo.addObserver(t => fileStampCache.invalidate(t.path))
|
||||
val persistFileStamps = extracted.get(watchPersistFileStamps)
|
||||
val cachingRepo: FileTreeRepository[FileAttributes] =
|
||||
if (persistFileStamps) repo else new FileStampRepository(fileStampCache, repo)
|
||||
try {
|
||||
val stateWithRepo = state.put(globalFileTreeRepository, repo)
|
||||
val stateWithRepo = state.put(globalFileTreeRepository, cachingRepo)
|
||||
val fullState =
|
||||
addLegacyWatchSetting(
|
||||
if (extracted.get(watchPersistFileStamps))
|
||||
stateWithRepo.put(persistentFileStampCache, fileStampCache)
|
||||
if (persistFileStamps) stateWithRepo.put(persistentFileStampCache, fileStampCache)
|
||||
else stateWithRepo
|
||||
)
|
||||
setup(fullState, commands) { (s, valid, invalid) =>
|
||||
|
|
@ -1091,4 +1094,17 @@ private[sbt] object Continuous extends DeprecatedContinuous {
|
|||
}
|
||||
}
|
||||
|
||||
private[sbt] class FileStampRepository(
|
||||
fileStampCache: FileStamp.Cache,
|
||||
underlying: FileTreeRepository[FileAttributes]
|
||||
) extends FileTreeRepository[FileAttributes] {
|
||||
def putIfAbsent(path: Path, stamper: FileStamper): (Option[FileStamp], Option[FileStamp]) =
|
||||
fileStampCache.putIfAbsent(path, stamper)
|
||||
override def list(path: Path): Seq[(Path, FileAttributes)] = underlying.list(path)
|
||||
override def addObserver(observer: Observer[FileEvent[FileAttributes]]): AutoCloseable =
|
||||
underlying.addObserver(observer)
|
||||
override def register(glob: Glob): Either[IOException, Observable[FileEvent[FileAttributes]]] =
|
||||
underlying.register(glob)
|
||||
override def close(): Unit = underlying.close()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,12 +253,12 @@ private[sbt] object FileStamp {
|
|||
case e => e.value
|
||||
}
|
||||
|
||||
def putIfAbsent(key: Path, stamper: FileStamper): Unit = {
|
||||
def putIfAbsent(key: Path, stamper: FileStamper): (Option[FileStamp], Option[FileStamp]) = {
|
||||
underlying.get(key) match {
|
||||
case null => updateImpl(key, stamper)
|
||||
case _ =>
|
||||
case null => (None, updateImpl(key, stamper))
|
||||
case Right(s) => (Some(s), None)
|
||||
case Left(_) => (None, None)
|
||||
}
|
||||
()
|
||||
}
|
||||
def update(key: Path, stamper: FileStamper): (Option[FileStamp], Option[FileStamp]) = {
|
||||
underlying.get(key) match {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import java.nio.file.{ Files, Path }
|
|||
|
||||
import sbt.Project._
|
||||
import sbt.internal.Clean.ToSeqPath
|
||||
import sbt.internal.Continuous.FileStampRepository
|
||||
import sbt.internal.util.{ AttributeKey, SourcePosition }
|
||||
import sbt.internal.{ Clean, Continuous, DynamicInput, SettingsGraph }
|
||||
import sbt.nio.FileStamp.{ fileStampJsonFormatter, pathJsonFormatter, _ }
|
||||
|
|
@ -319,10 +320,22 @@ private[sbt] object Settings {
|
|||
addTaskDefinition(Keys.inputFileStamps in scopedKey.scope := {
|
||||
val cache = (unmanagedFileStampCache in scopedKey.scope).value
|
||||
val stamper = (Keys.inputFileStamper in scopedKey.scope).value
|
||||
val stampFile: Path => Option[(Path, FileStamp)] =
|
||||
sbt.Keys.state.value.get(globalFileTreeRepository) match {
|
||||
case Some(repo: FileStampRepository) =>
|
||||
(path: Path) =>
|
||||
repo.putIfAbsent(path, stamper) match {
|
||||
case (None, Some(s)) =>
|
||||
cache.put(path, s)
|
||||
Some(path -> s)
|
||||
case _ => cache.getOrElseUpdate(path, stamper).map(path -> _)
|
||||
}
|
||||
case _ =>
|
||||
(path: Path) => cache.getOrElseUpdate(path, stamper).map(path -> _)
|
||||
}
|
||||
(Keys.allInputPathsAndAttributes in scopedKey.scope).value.flatMap {
|
||||
case (p, a) if a.isRegularFile && !Files.isHidden(p) =>
|
||||
cache.getOrElseUpdate(p, stamper).map(p -> _)
|
||||
case _ => None
|
||||
case (path, a) if a.isRegularFile && !Files.isHidden(path) => stampFile(path)
|
||||
case _ => None
|
||||
}
|
||||
})
|
||||
private[this] def outputsAndStamps[T: JsonFormat: ToSeqPath](
|
||||
|
|
|
|||
|
|
@ -576,7 +576,7 @@ object Watch {
|
|||
sbt.Keys.aggregate in watchTasks :== false,
|
||||
watchTriggeredMessage :== Watch.defaultOnTriggerMessage,
|
||||
watchForceTriggerOnAnyChange :== false,
|
||||
watchPersistFileStamps :== true,
|
||||
watchPersistFileStamps := (sbt.Keys.turbo in ThisBuild).value,
|
||||
watchTriggers :== Nil,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,4 @@ watchOnFileInputEvent := { (_, event: Watch.Event) =>
|
|||
else Watch.Trigger
|
||||
}
|
||||
|
||||
watchAntiEntropy := 0.millis
|
||||
watchAntiEntropy := 0.millis
|
||||
|
|
|
|||
Loading…
Reference in New Issue