Skip to content

Commit da0d52d

Browse files
In zarr streaming chunk response, answer 503 if dataset is missing (#8644)
* in zarr streaming chunk response, answer 400 if dataset is missing * changelog * do not log on zarr streaming chunk rpc failure * use service unavailable (503) instead of bad request (400) for this case --------- Co-authored-by: MichaelBuessemeyer <39529669+MichaelBuessemeyer@users.noreply.github.com>
1 parent a94f3e2 commit da0d52d

File tree

5 files changed

+19
-10
lines changed

5 files changed

+19
-10
lines changed

CHANGELOG.unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
4343
- Fixed that deletion of dataset would lead to an error. [#8639](https://github.com/scalableminds/webknossos/pull/8639)
4444
- Fixed a bug where merging annotations with large tree IDs could lead to an error. [#8643](https://github.com/scalableminds/webknossos/pull/8643)
4545
- Fixed that the segment stats were sometimes not displayed in the context menu. [#8645](https://github.com/scalableminds/webknossos/pull/8645)
46+
- Fixed a bug in zarr streaming where directly after the datastore startup, chunk responses would have status 404 (leading zarr clients to fill with fill_value). Now it will yield status 503, so that clients can retry or escalate this as an error. [#8644](https://github.com/scalableminds/webknossos/pull/8644)
4647

4748
### Removed
4849
- The old "Selective Segment Visibility" feature that allowed to only see the active and the hovered segment was removed. From now on the visibility of segments can be controlled with checkboxes in the segment list and with the "Hide unlisted segments" toggle in the layer settings. [#8546](https://github.com/scalableminds/webknossos/pull/8546)

webknossos-datastore/app/com/scalableminds/webknossos/datastore/controllers/ZarrStreamingController.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ class ZarrStreamingController @Inject()(
265265
for {
266266
(dataSource, dataLayer) <- dataSourceRepository.getDataSourceAndDataLayer(organizationId,
267267
datasetDirectoryName,
268-
dataLayerName) ~> NOT_FOUND
268+
dataLayerName) ~> SERVICE_UNAVAILABLE
269269
reorderedAdditionalAxes = dataLayer.additionalAxes.map(reorderAdditionalAxes)
270270
(x, y, z, additionalCoordinates) <- ZarrCoordinatesParser.parseNDimensionalDotCoordinates(
271271
coordinates,

webknossos-datastore/app/com/scalableminds/webknossos/datastore/rpc/RPCRequest.scala

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class RPCRequest(val id: Int, val url: String, wsClient: WSClient)(implicit ec:
2222

2323
var request: WSRequest = wsClient.url(url)
2424
private var verbose: Boolean = true
25+
private var logOnFailure: Boolean = true
2526
private var slowRequestLoggingThreshold = 2 minutes
2627

2728
def addQueryString(parameters: (String, String)*): RPCRequest = {
@@ -65,6 +66,12 @@ class RPCRequest(val id: Int, val url: String, wsClient: WSClient)(implicit ec:
6566
this
6667
}
6768

69+
def silentEvenOnFailure: RPCRequest = {
70+
verbose = false
71+
logOnFailure = false
72+
this
73+
}
74+
6875
def silentIf(condition: Boolean): RPCRequest = {
6976
if (condition) {
7077
verbose = false
@@ -203,7 +210,7 @@ class RPCRequest(val id: Int, val url: String, wsClient: WSClient)(implicit ec:
203210
}
204211
request
205212
.execute()
206-
.map { result =>
213+
.map { result: WSResponse =>
207214
val duration = Instant.since(before)
208215
val logSlow = verbose && duration > slowRequestLoggingThreshold
209216
if (Status.isSuccessful(result.status)) {
@@ -217,17 +224,17 @@ class RPCRequest(val id: Int, val url: String, wsClient: WSClient)(implicit ec:
217224
s" Status: ${result.status}.$durationLabel" +
218225
s" RequestBody: '$requestBodyPreview'" +
219226
s" ResponseBody: '$responseBodyPreview'"
220-
logger.error(verboseErrorMsg)
227+
if (logOnFailure) logger.error(verboseErrorMsg)
221228
val compactErrorMsg =
222229
s"Failed $debugInfo. Response: ${result.status} '$responseBodyPreview'"
223-
Failure(compactErrorMsg)
230+
Failure(compactErrorMsg) ~> result.status
224231
}
225232
}
226233
.recover {
227234
case e =>
228235
val errorMsg = s"Error sending $debugInfo: " +
229236
s"${e.getMessage}\n${e.getStackTrace.mkString("\n ")}"
230-
logger.error(errorMsg)
237+
if (logOnFailure) logger.error(errorMsg)
231238
Failure(errorMsg)
232239
}
233240
}

webknossos-datastore/app/com/scalableminds/webknossos/datastore/services/DSRemoteTracingstoreClient.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class DSRemoteTracingstoreClient @Inject()(
5858

5959
def getRawZarrCube(tracingId: String, mag: String, cxyz: String, tracingStoreUri: String)(
6060
implicit tc: TokenContext): Fox[Array[Byte]] =
61-
rpc(s"$tracingStoreUri/tracings/volume/zarr/$tracingId/$mag/$cxyz").silent.withTokenFromContext.getWithBytesResponse
61+
rpc(s"$tracingStoreUri/tracings/volume/zarr/$tracingId/$mag/$cxyz").silentEvenOnFailure.withTokenFromContext.getWithBytesResponse
6262

6363
def getDataLayerMagDirectoryContents(tracingId: String, mag: String, tracingStoreUri: String, zarrVersion: Int)(
6464
implicit tc: TokenContext): Fox[List[String]] =

webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/controllers/VolumeTracingZarrStreamingController.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ class VolumeTracingZarrStreamingController @Inject()(
321321
magParsed,
322322
Vec3Int(x, y, z),
323323
cubeSize,
324-
additionalCoordinates) ~> NOT_FOUND
324+
additionalCoordinates)
325325
} yield Ok(dataWithFallback)
326326
}
327327
}
@@ -348,9 +348,10 @@ class VolumeTracingZarrStreamingController @Inject()(
348348
version = None,
349349
additionalCoordinates = additionalCoordinates
350350
)
351-
(fallbackData, fallbackMissingBucketIndices) <- remoteDataStoreClient.getData(remoteFallbackLayer,
352-
List(request))
353-
_ <- Fox.fromBool(fallbackMissingBucketIndices.isEmpty) ?~> "No data at coordinations in fallback layer"
351+
(fallbackData, fallbackMissingBucketIndices) <- remoteDataStoreClient.getData(
352+
remoteFallbackLayer,
353+
List(request)) ~> SERVICE_UNAVAILABLE // return 503 if the request fails, assuming the datastore is still initializing
354+
_ <- Fox.fromBool(fallbackMissingBucketIndices.isEmpty) ?~> "No data at coordinations in fallback layer" ~> NOT_FOUND // return 404 if the request worked but data is empty
354355
} yield fallbackData
355356
} else Fox.successful(data)
356357
}

0 commit comments

Comments
 (0)