Skip to content

Commit

Permalink
fix(ui): add a configurable map action timeout on the server side
Browse files Browse the repository at this point in the history
  • Loading branch information
updraft0 committed Sep 24, 2024
1 parent 6d1c661 commit cfdd34f
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 9 deletions.
14 changes: 11 additions & 3 deletions db/src/main/scala/org/updraft0/controltower/db/query/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ import zio.{RIO, ZIO}

import java.sql.SQLException
import javax.sql.DataSource
import java.time.Instant
import java.time.{Duration, Instant}
import java.util.concurrent.TimeoutException

package object query:
type DbOperation[A] = ZIO[DataSource, SQLException, A]

val ctx = SqliteJsonZioJdbcContext(SnakeCase)

// final transaction at the end
def transaction[R, A](op: RIO[R & DataSource, A]): RIO[R & DataSource, A] = ctx.transaction(op)
/** Run an operation within a transaction with an optional timeout
*/
inline def transaction[R, A](
op: RIO[R & DataSource, A],
timeoutOpt: Option[Duration] = None
): RIO[R & DataSource, A] =
timeoutOpt match
case None => ZIO.logDebug("unbounded transaction") *> ctx.transaction(op)
case Some(timeout) => ctx.transaction(op).timeoutFail(new TimeoutException("query timed out"))(timeout)

inline def unixepoch = sql"unixepoch() * 1000".pure.as[Instant]
inline def unixepochMinusSeconds(inline seconds: Long) = sql"(unixepoch() - $seconds) * 1000".pure.as[Instant]
2 changes: 2 additions & 0 deletions server/src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ control-tower {
eol-connection-removal-interval: 270.minutes
# signatures and connections are physically removed (rather than soft deleted) after this interval
hard-deletion-interval: 10.days
# limit of any single query that affects a map
query-timeout: 2.seconds
}

# (optional) zio-http server configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import zio.*

import java.time.Instant
import java.util.UUID
import java.util.concurrent.TimeoutException

type MapEnv = MapConfig & javax.sql.DataSource & LocationTracker & MapPermissionTracker
type ShipTypeId = Int
Expand All @@ -24,7 +25,8 @@ case class MapConfig(
cleanupPeriod: Duration,
staleConnectionRemovalInterval: Duration,
eolConnectionRemovalInterval: Duration,
hardDeletionInterval: Duration
hardDeletionInterval: Duration,
queryTimeout: Duration
)

private[map] case class MapSolarSystem(
Expand All @@ -47,7 +49,8 @@ private[map] case class MapState(
CharacterId,
(SystemId, ShipTypeId)
], // manual cache of location systems - used to prevent too many updates
ref: MapRef
ref: MapRef,
queryTimeout: Duration
):
// TODO this should be loaded from DB
val displayType: model.MapDisplayType = model.MapDisplayType.Manual
Expand Down Expand Up @@ -191,15 +194,17 @@ object MapState:
systems: List[MapSystemWithAll],
connections: List[MapWormholeConnectionWithSigs],
connectionRanks: List[MapWormholeConnectionRank],
ref: MapRef
ref: MapRef,
queryTimeout: Duration
): MapState =
new MapState(
systems.map(msa => msa.sys.systemId -> msa).toMap,
connections.map(whc => whc.connection.id -> whc).toMap,
connectionRanks.map(whr => whr.connectionId -> whr).toMap,
locations = Map.empty,
locationsOnline = Map.empty,
ref
ref,
queryTimeout = queryTimeout
)

case class MapSessionId(characterId: CharacterId, sessionId: UUID) derives CanEqual
Expand Down Expand Up @@ -341,7 +346,7 @@ object MapEntity extends ReactiveEntity[MapEnv, MapId, MapState, Identified[MapR
.repeat(Schedule.fixed(config.cleanupPeriod))
.ignoreLogged
.forkScoped
yield MapState(systems, connections, connectionRanks, mapRef)).orDie
yield MapState(systems, connections, connectionRanks, mapRef, config.queryTimeout)).orDie

override def handle(
mapId: MapId,
Expand Down Expand Up @@ -432,8 +437,14 @@ object MapEntity extends ReactiveEntity[MapEnv, MapId, MapState, Identified[MapR
// fall-through case
case Identified(None, _) =>
ZIO.logWarning("non-identified request not processed").as(state -> Chunk.empty)
,
timeoutOpt = Some(state.queryTimeout)
)) @@ Log.MapId(mapId)
)
.logError("map operation failed")
.catchSome { case _: TimeoutException =>
ZIO.succeed((state, Chunk(Identified(in.sessionId, MapResponse.Error("Action took too long, try again")))))
}
.orDie

private inline def identified[R, E, A](sid: MapSessionId, op: String, f: ZIO[R, E, A]): ZIO[R, E, A] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ object MapReactiveSpec extends ZIOSpecDefault:
TempDb.empty,
TestLocationTracker.empty,
TestPermissionTracker.empty,
ZLayer.succeed(MapConfig(1.day, 1.day, 1.day, 1.day))
ZLayer.succeed(MapConfig(1.day, 1.day, 1.day, 1.day, 5.seconds))
// uncomment to enable debug logging in tests
// consoleLogger(ConsoleLoggerConfig.apply(LogFormat.colored, LogFilter.LogLevelByNameConfig(LogLevel.Debug))),
// Slf4jBridge.init(LogFilter.acceptAll)
Expand Down

0 comments on commit cfdd34f

Please # to comment.