/** * Tries to parse a segment id from the given String representation, or returns null on failure. If returns a non-null * {@code SegmentId} object, calling {@link #toString()} on the latter is guaranteed to return a string equal to the * argument string of the {@code tryParse()} call. * * It is possible that this method may incorrectly parse a segment id, for example if the dataSource name in the * segment id contains a DateTime parseable string such as 'datasource_2000-01-01T00:00:00.000Z' and dataSource was * provided as 'datasource'. The desired behavior in this case would be to return null since the identifier does not * actually belong to the provided dataSource but a non-null result would be returned. This is an edge case that would * currently only affect paged select queries with a union dataSource of two similarly-named dataSources as in the * given example. * * Another source of ambiguity is the end of a segment id like '_123' - it could always be interpreted either as the * partitionNum of the segment id, or as the end of the version, with the implicit partitionNum of 0. This method * prefers the first iterpretation. To iterate all possible parsings of a segment id, use {@link * #iteratePossibleParsingsWithDataSource}. * * @param dataSource the dataSource corresponding to this segment id * @param segmentId segment id * @return a {@link SegmentId} object if the segment id could be parsed, null otherwise */ @Nullable public static SegmentId tryParse(String dataSource, String segmentId) { List<SegmentId> possibleParsings = iteratePossibleParsingsWithDataSource(dataSource, segmentId); return possibleParsings.isEmpty() ? null : possibleParsings.get(0); }
List<SegmentId> probableParsings = iteratePossibleParsingsWithDataSource(probableDataSource, segmentId); Iterable<SegmentId> otherPossibleParsings = () -> IntStream .range(1, splits.size() - 3) .mapToObj(dataSourceDelimiterOrder -> DELIMITER_JOINER.join(splits.subList(0, dataSourceDelimiterOrder))) .filter(dataSource -> dataSource.length() != probableDataSource.length()) .flatMap(dataSource -> iteratePossibleParsingsWithDataSource(dataSource, segmentId).stream()) .iterator(); return Iterables.concat(probableParsings, otherPossibleParsings); .mapToObj(dataSourceDelimiterOrder -> { String dataSource = DELIMITER_JOINER.join(splits.subList(0, dataSourceDelimiterOrder)); return iteratePossibleParsingsWithDataSource(dataSource, segmentId); }) .flatMap(List::stream)
@GET @Path("/{dataSourceName}/segments/{segmentId}") @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(DatasourceResourceFilter.class) public Response getSegmentDataSourceSegment( @PathParam("dataSourceName") String dataSourceName, @PathParam("segmentId") String segmentId ) { ImmutableDruidDataSource dataSource = getDataSource(dataSourceName); if (dataSource == null) { return Response.noContent().build(); } for (SegmentId possibleSegmentId : SegmentId.iteratePossibleParsingsWithDataSource(dataSourceName, segmentId)) { Pair<DataSegment, Set<String>> retVal = getServersWhereSegmentIsServed(possibleSegmentId); if (retVal != null) { return Response.ok(ImmutableMap.of("metadata", retVal.lhs, "servers", retVal.rhs)).build(); } } return Response.noContent().build(); }
@GET @Path("/datasources/{dataSourceName}/segments/{segmentId}") @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(DatasourceResourceFilter.class) public Response getDatabaseSegmentDataSourceSegment( @PathParam("dataSourceName") String dataSourceName, @PathParam("segmentId") String segmentId ) { ImmutableDruidDataSource dataSource = metadataSegmentManager.getDataSource(dataSourceName); if (dataSource == null) { return Response.status(Response.Status.NOT_FOUND).build(); } for (SegmentId possibleSegmentId : SegmentId.iteratePossibleParsingsWithDataSource(dataSourceName, segmentId)) { DataSegment segment = dataSource.getSegment(possibleSegmentId); if (segment != null) { return Response.status(Response.Status.OK).entity(segment).build(); } } return Response.status(Response.Status.NOT_FOUND).build(); } }
@Override public boolean removeSegment(String dataSourceName, final String segmentId) { try { final boolean removed = removeSegmentFromTable(segmentId); // Call iteratePossibleParsingsWithDataSource() outside of dataSources.computeIfPresent() because the former is a // potentially expensive operation, while lambda to be passed into computeIfPresent() should preferably run fast. List<SegmentId> possibleSegmentIds = SegmentId.iteratePossibleParsingsWithDataSource(dataSourceName, segmentId); dataSources.computeIfPresent( dataSourceName, (dsName, dataSource) -> { for (SegmentId possibleSegmentId : possibleSegmentIds) { if (dataSource.removeSegment(possibleSegmentId) != null) { break; } } // Returning null from the lambda here makes the ConcurrentHashMap to remove the current entry. //noinspection ReturnOfNull return dataSource.isEmpty() ? null : dataSource; } ); return removed; } catch (Exception e) { log.error(e, e.toString()); return false; } }
/** * Tests that {@link SegmentId#tryExtractMostProbableDataSource} successfully extracts data sources from some * reasonable segment ids. */ @Test public void testTryParseHeuristically() { List<String> segmentIds = Arrays.asList( "datasource_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_ver_0_1", "datasource_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_ver", "datasource_1_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_ver_0_1", "datasource_1_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_ver", "datasource_2015-01-01T00:00:00.000Z_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_ver_0_1", "datasource_2015-01-01T00:00:00.000Z_2015-01-02T00:00:00.000Z_2015-01-03T00:00:00.000Z_ver" ); for (String segmentId : segmentIds) { String dataSource = SegmentId.tryExtractMostProbableDataSource(segmentId); Assert.assertTrue("datasource".equals(dataSource) || "datasource_1".equals(dataSource)); Assert.assertTrue(!SegmentId.iteratePossibleParsingsWithDataSource(dataSource, segmentId).isEmpty()); } }