Skip to content

Commit

Permalink
Fix for SQLite DB busy error on Azure (#1395)
Browse files Browse the repository at this point in the history
depending on an environmental variable is used either 
default locking mode or a mode that uses flock syscall
  • Loading branch information
lolczak authored and iamrecursion committed Feb 11, 2021
1 parent 8f8ec81 commit 0010ba7
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.enso.languageserver.boot

/** Signal where the lang. server is deployed.
*/
sealed trait DeploymentType

object DeploymentType {

/** Desktop deployment.
*/
case object Desktop extends DeploymentType

/** Azure deployment.
*/
case object Azure extends DeploymentType

/** Determines the current deployment type from environment variables.
* @return the current deployment type
*/
def fromEnvironment(): DeploymentType = {
if (sys.env.contains(DeploymentTypeVariableName)) {
val value = sys.env(DeploymentTypeVariableName)
fromString(value)
} else {
Desktop
}
}

/** Determines a current deployment type from a string value.
* @return a deployment type
*/
def fromString(value: String): DeploymentType =
value.toLowerCase.trim match {
case "desktop" | "" => Desktop
case "azure" => Azure
}

private lazy val DeploymentTypeVariableName = "DEPLOYMENT_TYPE"

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.net.URI

import akka.actor.ActorSystem
import org.enso.jsonrpc.JsonRpcServer
import org.enso.languageserver.boot.DeploymentType.{Azure, Desktop}
import org.enso.languageserver.capability.CapabilityRouter
import org.enso.languageserver.data._
import org.enso.languageserver.effect.ZioExec
Expand Down Expand Up @@ -34,6 +35,7 @@ import org.enso.languageserver.util.binary.BinaryEncoder
import org.enso.loggingservice.{JavaLoggingLogHandler, LogLevel}
import org.enso.polyglot.{LanguageInfo, RuntimeOptions, RuntimeServerInfo}
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
import org.enso.searcher.sqlite.LockingMode
import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator}
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.io.MessageEndpoint
Expand Down Expand Up @@ -84,7 +86,19 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: LogLevel) {
log.trace("Created ActorSystem")

val sqlDatabase =
SqlDatabase(languageServerConfig.directories.suggestionsDatabaseFile)
DeploymentType.fromEnvironment() match {
case Desktop =>
SqlDatabase(
languageServerConfig.directories.suggestionsDatabaseFile.toString
)

case Azure =>
SqlDatabase(
languageServerConfig.directories.suggestionsDatabaseFile.toString,
Some(LockingMode.UnixFlock)
)
}

val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)(system.dispatcher)
val versionsRepo = new SqlVersionsRepo(sqlDatabase)(system.dispatcher)
log.trace("Created SQL Repos")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ import org.enso.languageserver.session.JsonSession
import org.enso.languageserver.session.SessionRouter.DeliverToJsonController
import org.enso.polyglot.data.Tree
import org.enso.polyglot.runtime.Runtime.Api
import org.enso.searcher.{FileVersionsRepo, SuggestionsRepo}
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo, SqlVersionsRepo}
import org.enso.searcher.{FileVersionsRepo, SuggestionsRepo}
import org.enso.testkit.RetrySpec
import org.enso.text.{ContentVersion, Sha3_224VersionCalculator}
import org.enso.text.editing.model.Position
import org.enso.text.{ContentVersion, Sha3_224VersionCalculator}
import org.scalatest.BeforeAndAfterAll
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike
Expand Down Expand Up @@ -594,10 +594,12 @@ class SuggestionsHandlerSpec
): Unit = {
val testContentRoot = Files.createTempDirectory(null).toRealPath()
sys.addShutdownHook(FileUtils.deleteQuietly(testContentRoot.toFile))
val config = newConfig(testContentRoot.toFile)
val router = TestProbe("session-router")
val connector = TestProbe("runtime-connector")
val sqlDatabase = SqlDatabase(config.directories.suggestionsDatabaseFile)
val config = newConfig(testContentRoot.toFile)
val router = TestProbe("session-router")
val connector = TestProbe("runtime-connector")
val sqlDatabase = SqlDatabase(
config.directories.suggestionsDatabaseFile.toString
)
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)
val versionsRepo = new SqlVersionsRepo(sqlDatabase)
val handler = newSuggestionsHandler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ class BaseServerTest extends JsonRpcServerTestKit {
InputRedirectionController.props(stdIn, stdInSink, sessionRouter)
)

override def clientControllerFactory: ClientControllerFactory = {

val zioExec = ZioExec(zio.Runtime.default)
val sqlDatabase = SqlDatabase(config.directories.suggestionsDatabaseFile)
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)(system.dispatcher)
val versionsRepo = new SqlVersionsRepo(sqlDatabase)(system.dispatcher)
val zioExec = ZioExec(zio.Runtime.default)
val sqlDatabase =
SqlDatabase(config.directories.suggestionsDatabaseFile.toString)
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)(system.dispatcher)
val versionsRepo = new SqlVersionsRepo(sqlDatabase)(system.dispatcher)

override def clientControllerFactory: ClientControllerFactory = {
val fileManager =
system.actorOf(FileManager.props(config, new FileSystem, zioExec))
val bufferRegistry =
Expand Down
1 change: 1 addition & 0 deletions lib/scala/searcher/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ searcher {
driver = "org.sqlite.JDBC"
connectionPool = disabled
properties.journal_mode = "wal"
properties.locking_mode = "EXCLUSIVE"
numThreads = 1
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.enso.searcher.sql

import java.io.File

import com.typesafe.config.{Config, ConfigFactory}
import org.enso.searcher.Database
import org.enso.searcher.sqlite.LockingMode
import slick.dbio.DBIO
import slick.jdbc.SQLiteProfile
import slick.jdbc.SQLiteProfile.api._
Expand Down Expand Up @@ -43,24 +42,31 @@ object SqlDatabase {
* @param filename the database file path
* @return new sql database instance
*/
def apply(filename: File): SqlDatabase =
apply(filename.toString)

/** Create [[SqlDatabase]] instance.
*
* @param filename the database file path
* @return new sql database instance
*/
def apply(filename: String): SqlDatabase = {
def apply(
filename: String,
maybeLockingMode: Option[LockingMode] = None
): SqlDatabase = {
val config = ConfigFactory
.parseString(s"""$configPath.url = "${jdbcUrl(filename)}"""")
.parseString(
s"""$configPath.url = "${jdbcUrl(filename, maybeLockingMode)}""""
)
.withFallback(ConfigFactory.load())
new SqlDatabase(Some(config))
}

/** Create JDBC URL from the file path. */
private def jdbcUrl(filename: String): String =
s"jdbc:sqlite:${escapePath(filename)}"
private def jdbcUrl(
filename: String,
maybeLockingMode: Option[LockingMode]
): String = {
maybeLockingMode match {
case None =>
s"jdbc:sqlite:${escapePath(filename)}"

case Some(lockingMode) =>
s"jdbc:sqlite:file:${escapePath(filename)}?vfs=${lockingMode.name}"
}
}

/** Escape Windows path. */
private def escapePath(path: String): String =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.enso.searcher.sqlite

/** A mode used to handle file locking in SQLite */
case class LockingMode private (name: String)

object LockingMode {

/** Default mode that uses POSIX advisory locks.
*/
val UnixPosix = LockingMode("unix")

/** It obtains and holds an exclusive lock on database files,
* preventing other processes from accessing the database.
* It uses the `flock` system call.
*/
val UnixFlock = LockingMode("unix-excl")

/** It uses dot-file locking rather than POSIX advisory locks. */
val UnixDotFile = LockingMode("unix-dotfile")

/** All file locking operations are no-ops. */
val UnixNone = LockingMode("unix-none")

}
11 changes: 5 additions & 6 deletions tools/ci/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ ADD runtime.jar /opt/enso/runtime.jar
RUN chown -hR enso:enso /opt/enso
RUN chmod -R u=rX,g=rX /opt/enso

RUN mkdir -p /home/enso/workspace
RUN chown -hR enso:enso /home/enso/workspace
RUN chmod -R u=rwX,g=rwX /home/enso/workspace
RUN mkdir -p /volumes
RUN chown -hR enso:enso /volumes
RUN chmod -R u=rwX,g=rwX /volumes

USER enso:enso

Expand All @@ -24,7 +24,6 @@ ENTRYPOINT ["java", "-jar", "-Dtruffle.class.path.append=runtime.jar", "-Dpolyg
EXPOSE 30001
EXPOSE 30002

VOLUME /home/enso/workspace

CMD ["--server", "--rpc-port", "30001", "--data-port", "30002", "--root-id", "00000000-0000-0000-0000-000000000001", "--path", "/home/enso/workspace", "--interface", "0.0.0.0"]
VOLUME /volumes/workspace

CMD ["--server", "--daemon", "--rpc-port", "30001", "--data-port", "30002", "--root-id", "00000000-0000-0000-0000-000000000001", "--path", "/volumes/workspace", "--interface", "0.0.0.0", "--log-level", "INFO"]

0 comments on commit 0010ba7

Please sign in to comment.