Skip to content

Commit bb3397f

Browse files
committed
refactor with MergedFieldData
1 parent 30ce01e commit bb3397f

File tree

1 file changed

+123
-114
lines changed

1 file changed

+123
-114
lines changed

solr/core/src/java/org/apache/solr/handler/admin/LukeRequestHandler.java

Lines changed: 123 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -245,21 +245,53 @@ && handleDistributed(req, rsp)) {
245245
rsp.setHttpCaching(false);
246246
}
247247

248-
/** Tracks the first-seen valid properties of a field across shards. */
249-
private static class ExpectedFieldConfig {
248+
/** Per-field accumulation state across shards: merged response data and index flags tracking. */
249+
private static class MergedFieldData {
250+
final SimpleOrderedMap<Object> merged = new SimpleOrderedMap<>();
251+
final String originalShardAddr;
252+
private Object indexFlags;
253+
private String indexFlagsShardAddr;
254+
255+
MergedFieldData(String shardAddr, Object indexFlags) {
256+
this.originalShardAddr = shardAddr;
257+
if (indexFlags != null) {
258+
this.indexFlags = indexFlags;
259+
this.indexFlagsShardAddr = shardAddr;
260+
}
261+
}
262+
}
263+
264+
private static class ShardData {
250265
final String shardAddr;
251-
final LukeResponse.FieldInfo fieldInfo;
252-
Object indexFlags;
253-
String indexFlagsShardAddr;
266+
final Map<String, LukeResponse.FieldInfo> shardFieldInfo;
267+
private NamedList<Object> indexInfo;
268+
private SimpleOrderedMap<Object> detailedFields;
254269

255-
ExpectedFieldConfig(String shardAddr, LukeResponse.FieldInfo fieldInfo) {
270+
ShardData(String shardAddr, Map<String, LukeResponse.FieldInfo> shardFieldInfo) {
256271
this.shardAddr = shardAddr;
257-
this.fieldInfo = fieldInfo;
258-
Object flags = fieldInfo.getExtras().get(KEY_INDEX_FLAGS);
259-
if (flags != null) {
260-
this.indexFlags = flags;
261-
this.indexFlagsShardAddr = shardAddr;
272+
this.shardFieldInfo = shardFieldInfo;
273+
}
274+
275+
void setIndexInfo(NamedList<Object> indexInfo) {
276+
this.indexInfo = indexInfo;
277+
}
278+
279+
void addDetailedFieldInfo(String fieldName, SimpleOrderedMap<Object> fieldStats) {
280+
if (detailedFields == null) {
281+
detailedFields = new SimpleOrderedMap<>();
282+
}
283+
detailedFields.add(fieldName, fieldStats);
284+
}
285+
286+
SimpleOrderedMap<Object> toResponseEntry() {
287+
SimpleOrderedMap<Object> entry = new SimpleOrderedMap<>();
288+
if (indexInfo != null) {
289+
entry.add(RSP_INDEX, indexInfo);
290+
}
291+
if (detailedFields != null) {
292+
entry.add(RSP_FIELDS, detailedFields);
262293
}
294+
return entry;
263295
}
264296
}
265297

@@ -268,8 +300,7 @@ private static class ExpectedFieldConfig {
268300
* short-circuited (e.g. single-shard collection) and the caller should fall through to local
269301
* logic.
270302
*/
271-
private boolean handleDistributed(SolrQueryRequest req, SolrQueryResponse rsp)
272-
throws IOException {
303+
private boolean handleDistributed(SolrQueryRequest req, SolrQueryResponse rsp) {
273304
ShardHandler shardHandler = shardHandlerFactory.getShardHandler();
274305
ResponseBuilder rb = new ResponseBuilder(req, rsp, Collections.emptyList());
275306
shardHandler.prepDistributed(rb);
@@ -324,9 +355,7 @@ private void mergeDistributedResponses(SolrQueryResponse rsp, List<ShardResponse
324355
long totalDeletedDocs = 0;
325356
int totalSegmentCount = 0;
326357

327-
Map<String, SimpleOrderedMap<Object>> mergedFields = new HashMap<>();
328-
Map<String, ExpectedFieldConfig> expectedFieldConfigs = new HashMap<>();
329-
SimpleOrderedMap<Object> shardsInfo = new SimpleOrderedMap<>();
358+
Map<String, MergedFieldData> mergedFields = new HashMap<>();
330359

331360
if (!responses.isEmpty()) {
332361
ShardResponse firstRsp = responses.getFirst();
@@ -346,12 +375,14 @@ private void mergeDistributedResponses(SolrQueryResponse rsp, List<ShardResponse
346375
}
347376
}
348377

378+
List<ShardData> shardDataList = new ArrayList<>();
379+
349380
for (ShardResponse srsp : responses) {
350-
String shardAddr = shardAddress(srsp);
351381
NamedList<Object> shardRsp = srsp.getSolrResponse().getResponse();
352382
LukeResponse lukeRsp = new LukeResponse();
353383
lukeRsp.setResponse(shardRsp);
354-
SimpleOrderedMap<Object> perShardEntry = new SimpleOrderedMap<>();
384+
ShardData shardData = new ShardData(shardAddress(srsp), lukeRsp.getFieldInfo());
385+
355386
NamedList<Object> shardIndex = lukeRsp.getIndexInfo();
356387
if (shardIndex != null) {
357388
totalNumDocs += Optional.ofNullable(lukeRsp.getNumDocsAsLong()).orElse(0L);
@@ -360,43 +391,19 @@ private void mergeDistributedResponses(SolrQueryResponse rsp, List<ShardResponse
360391
Number segCount = (Number) shardIndex.get(KEY_SEGMENT_COUNT);
361392
totalSegmentCount += segCount != null ? segCount.intValue() : 0;
362393

363-
perShardEntry.add(RSP_INDEX, shardIndex);
394+
shardData.setIndexInfo(shardIndex);
364395
}
365396

366-
Map<String, LukeResponse.FieldInfo> shardFieldInfo = lukeRsp.getFieldInfo();
367-
if (shardFieldInfo != null) {
368-
SimpleOrderedMap<Object> perShardFields = new SimpleOrderedMap<>();
369-
370-
for (Map.Entry<String, LukeResponse.FieldInfo> entry : shardFieldInfo.entrySet()) {
371-
String fieldName = entry.getKey();
372-
LukeResponse.FieldInfo fi = entry.getValue();
373-
374-
SimpleOrderedMap<Object> merged =
375-
mergedFields.computeIfAbsent(fieldName, k -> new SimpleOrderedMap<>());
376-
377-
mergeShardField(shardAddr, fi, merged, expectedFieldConfigs);
378-
379-
// Detailed stats — kept per-shard, not merged
380-
NamedList<Integer> topTerms = fi.getTopTerms();
381-
Object histogram = fi.getExtras().get(KEY_HISTOGRAM);
397+
processShardFields(shardData, mergedFields);
398+
shardDataList.add(shardData);
399+
}
382400

383-
if (topTerms != null || fi.getDistinct() > 0 || histogram != null) {
384-
perShardEntry.putIfAbsent(RSP_FIELDS, perShardFields);
385-
SimpleOrderedMap<Object> detailedFieldInfo = new SimpleOrderedMap<>();
386-
if (topTerms != null) {
387-
detailedFieldInfo.add(KEY_TOP_TERMS, topTerms);
388-
}
389-
if (fi.getDistinct() > 0) {
390-
detailedFieldInfo.add(KEY_DISTINCT, fi.getDistinct());
391-
}
392-
if (histogram != null) {
393-
detailedFieldInfo.add(KEY_HISTOGRAM, histogram);
394-
}
395-
perShardFields.add(fieldName, detailedFieldInfo);
396-
}
397-
}
401+
SimpleOrderedMap<Object> shardsInfo = new SimpleOrderedMap<>();
402+
for (ShardData sd : shardDataList) {
403+
SimpleOrderedMap<Object> entry = sd.toResponseEntry();
404+
if (!entry.isEmpty()) {
405+
shardsInfo.add(sd.shardAddr, entry);
398406
}
399-
shardsInfo.add(shardAddr, perShardEntry);
400407
}
401408

402409
SimpleOrderedMap<Object> mergedIndex = new SimpleOrderedMap<>();
@@ -408,109 +415,111 @@ private void mergeDistributedResponses(SolrQueryResponse rsp, List<ShardResponse
408415

409416
if (!mergedFields.isEmpty()) {
410417
SimpleOrderedMap<Object> mergedFieldsNL = new SimpleOrderedMap<>();
411-
for (Map.Entry<String, SimpleOrderedMap<Object>> entry : mergedFields.entrySet()) {
412-
mergedFieldsNL.add(entry.getKey(), entry.getValue());
418+
for (Map.Entry<String, MergedFieldData> entry : mergedFields.entrySet()) {
419+
mergedFieldsNL.add(entry.getKey(), entry.getValue().merged);
413420
}
414421
rsp.add(RSP_FIELDS, mergedFieldsNL);
415422
}
416423

417424
rsp.add(RSP_SHARDS, shardsInfo);
418425
}
419426

427+
private void processShardFields(
428+
ShardData shardData, Map<String, MergedFieldData> mergedFields) {
429+
if (shardData.shardFieldInfo == null) {
430+
return;
431+
}
432+
for (Map.Entry<String, LukeResponse.FieldInfo> entry : shardData.shardFieldInfo.entrySet()) {
433+
String fieldName = entry.getKey();
434+
LukeResponse.FieldInfo fi = entry.getValue();
435+
436+
mergeShardField(shardData.shardAddr, fi, mergedFields);
437+
438+
// Detailed stats — kept per-shard, not merged
439+
NamedList<Integer> topTerms = fi.getTopTerms();
440+
Object histogram = fi.getExtras().get(KEY_HISTOGRAM);
441+
442+
if (topTerms != null || fi.getDistinct() > 0 || histogram != null) {
443+
SimpleOrderedMap<Object> detailedFieldInfo = new SimpleOrderedMap<>();
444+
if (topTerms != null) {
445+
detailedFieldInfo.add(KEY_TOP_TERMS, topTerms);
446+
}
447+
if (fi.getDistinct() > 0) {
448+
detailedFieldInfo.add(KEY_DISTINCT, fi.getDistinct());
449+
}
450+
if (histogram != null) {
451+
detailedFieldInfo.add(KEY_HISTOGRAM, histogram);
452+
}
453+
shardData.addDetailedFieldInfo(fieldName, detailedFieldInfo);
454+
}
455+
}
456+
}
457+
420458
private void mergeShardField(
421-
String shardAddr,
422-
LukeResponse.FieldInfo fi,
423-
SimpleOrderedMap<Object> merged,
424-
Map<String, ExpectedFieldConfig> expectedFieldConfigs) {
459+
String shardAddr, LukeResponse.FieldInfo fi, Map<String, MergedFieldData> mergedFields) {
425460

426461
String fieldName = fi.getName();
427-
ExpectedFieldConfig origin = expectedFieldConfigs.get(fieldName);
428-
if (origin == null) {
429-
origin = new ExpectedFieldConfig(shardAddr, fi);
430-
expectedFieldConfigs.put(fieldName, origin);
462+
Object indexFlags = fi.getExtras().get(KEY_INDEX_FLAGS);
463+
464+
MergedFieldData fieldData = mergedFields.get(fieldName);
465+
if (fieldData == null) {
466+
fieldData = new MergedFieldData(shardAddr, indexFlags);
467+
mergedFields.put(fieldName, fieldData);
468+
431469
// First shard to report this field: populate merged with schema-derived attrs
432-
merged.add(KEY_TYPE, fi.getType());
433-
merged.add(KEY_SCHEMA_FLAGS, fi.getSchema());
470+
fieldData.merged.add(KEY_TYPE, fi.getType());
471+
fieldData.merged.add(KEY_SCHEMA_FLAGS, fi.getSchema());
434472
Object dynBase = fi.getExtras().get(KEY_DYNAMIC_BASE);
435473
if (dynBase != null) {
436-
merged.add(KEY_DYNAMIC_BASE, dynBase);
474+
fieldData.merged.add(KEY_DYNAMIC_BASE, dynBase);
437475
}
438-
if (origin.indexFlags != null) {
439-
merged.add(KEY_INDEX_FLAGS, origin.indexFlags);
476+
if (fieldData.indexFlags != null) {
477+
fieldData.merged.add(KEY_INDEX_FLAGS, fieldData.indexFlags);
440478
}
441-
} else {
442-
// Subsequent shards: validate consistency
443-
validateFieldAttr(
444-
fieldName,
445-
KEY_TYPE,
446-
fi.getType(),
447-
origin.fieldInfo.getType(),
448-
shardAddr,
449-
origin.shardAddr);
450-
validateFieldAttr(
451-
fieldName,
452-
KEY_SCHEMA_FLAGS,
453-
fi.getSchema(),
454-
origin.fieldInfo.getSchema(),
455-
shardAddr,
456-
origin.shardAddr);
457-
validateFieldAttr(
458-
fieldName,
459-
KEY_DYNAMIC_BASE,
460-
fi.getExtras().get(KEY_DYNAMIC_BASE),
461-
origin.fieldInfo.getExtras().get(KEY_DYNAMIC_BASE),
462-
shardAddr,
463-
origin.shardAddr);
464-
465-
Object indexFlags = fi.getExtras().get(KEY_INDEX_FLAGS);
466-
if (indexFlags != null) {
467-
if (origin.indexFlags == null) {
468-
origin.indexFlags = indexFlags;
469-
origin.indexFlagsShardAddr = shardAddr;
470-
merged.add(KEY_INDEX_FLAGS, indexFlags);
471-
} else {
472-
validateFieldAttr(
473-
fieldName,
474-
KEY_INDEX_FLAGS,
475-
indexFlags,
476-
origin.indexFlags,
477-
shardAddr,
478-
origin.indexFlagsShardAddr);
479-
}
479+
} else if (indexFlags != null) {
480+
// Subsequent shards: validate index flags consistency
481+
if (fieldData.indexFlags == null) {
482+
fieldData.indexFlags = indexFlags;
483+
fieldData.indexFlagsShardAddr = shardAddr;
484+
fieldData.merged.add(KEY_INDEX_FLAGS, indexFlags);
485+
} else {
486+
validateFieldAttr(
487+
fieldName, KEY_INDEX_FLAGS, indexFlags, fieldData.indexFlags,
488+
shardAddr, fieldData.indexFlagsShardAddr);
480489
}
481490
}
482491

483492
Long docsAsLong = fi.getDocsAsLong();
484493
if (docsAsLong != null && docsAsLong > 0) {
485-
merged.compute(
494+
fieldData.merged.compute(
486495
KEY_DOCS_AS_LONG, (key, val) -> val == null ? docsAsLong : (Long) val + docsAsLong);
487496
}
488497
}
489498

490-
/** Validates that a schema-derived attribute value is identical across shards. */
499+
/** Validates that a field attribute value is identical across shards. */
491500
private void validateFieldAttr(
492501
String fieldName,
493502
String attrName,
494503
Object currentVal,
495-
Object originVal,
504+
Object expectedVal,
496505
String currentShardAddr,
497-
String originShardAddr) {
498-
if (currentVal == null && originVal == null) {
506+
String expectedShardAddr) {
507+
if (currentVal == null && expectedVal == null) {
499508
return;
500509
}
501510
String currentStr = currentVal != null ? currentVal.toString() : null;
502-
String originStr = originVal != null ? originVal.toString() : null;
503-
if (!Objects.equals(currentStr, originStr)) {
511+
String expectedStr = expectedVal != null ? expectedVal.toString() : null;
512+
if (!Objects.equals(currentStr, expectedStr)) {
504513
throw new SolrException(
505514
ErrorCode.SERVER_ERROR,
506515
"Field '"
507516
+ fieldName
508517
+ "' has inconsistent '"
509518
+ attrName
510519
+ "' across shards: '"
511-
+ originStr
520+
+ expectedStr
512521
+ "' (from "
513-
+ originShardAddr
522+
+ expectedShardAddr
514523
+ ") vs '"
515524
+ currentStr
516525
+ "' (from "

0 commit comments

Comments
 (0)