Skip to content

Commit ebc31c8

Browse files
committed
fix(core): stabilize hstore range index ordering
1 parent a183571 commit ebc31c8

1 file changed

Lines changed: 183 additions & 2 deletions

File tree

hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/backend/tx/GraphIndexTransaction.java

Lines changed: 183 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,9 @@ private void storeSelectedIndexField(IndexLabel indexLabel,
651651

652652
@Watched(prefix = "index")
653653
private IdHolder doIndexQuery(IndexLabel indexLabel, ConditionQuery query) {
654+
if (this.needHstoreRangeIndexOrder(indexLabel)) {
655+
return this.doHstoreRangeIndexQuery(indexLabel, query);
656+
}
654657
if (!query.paging()) {
655658
return this.doIndexQueryBatch(indexLabel, query);
656659
} else {
@@ -660,6 +663,184 @@ private IdHolder doIndexQuery(IndexLabel indexLabel, ConditionQuery query) {
660663
}
661664
}
662665

666+
private boolean needHstoreRangeIndexOrder(IndexLabel indexLabel) {
667+
return this.store().provider().isHstore() &&
668+
indexLabel.indexType().isRange();
669+
}
670+
671+
private IdHolder doHstoreRangeIndexQuery(IndexLabel indexLabel,
672+
ConditionQuery query) {
673+
if (!query.paging()) {
674+
if (query.noLimitAndOffset()) {
675+
return this.doIndexQueryBatch(indexLabel, query);
676+
}
677+
Set<Id> ids = this.querySortedRangeIndexIds(indexLabel, query);
678+
return this.newSortedRangeIndexBatchHolder(query, ids);
679+
}
680+
return new PagingIdHolder(query, q -> {
681+
return this.querySortedRangeIndexPage(indexLabel, q);
682+
});
683+
}
684+
685+
private BatchIdHolder newSortedRangeIndexBatchHolder(ConditionQuery query,
686+
Set<Id> ids) {
687+
List<Id> idList = new ArrayList<>(ids);
688+
return new BatchIdHolder(query, Collections.emptyIterator(), batch -> {
689+
throw new IllegalStateException("Unexpected sorted index fetcher");
690+
}) {
691+
private int offset = 0;
692+
693+
@Override
694+
public boolean hasNext() {
695+
return this.offset < idList.size();
696+
}
697+
698+
@Override
699+
public IdHolder next() {
700+
if (!this.hasNext()) {
701+
throw new java.util.NoSuchElementException();
702+
}
703+
return this;
704+
}
705+
706+
@Override
707+
public PageIds fetchNext(String page, long batchSize) {
708+
E.checkArgument(page == null,
709+
"Not support page parameter by BatchIdHolder");
710+
if (!this.hasNext()) {
711+
return PageIds.EMPTY;
712+
}
713+
714+
int end;
715+
if (batchSize == Query.NO_LIMIT) {
716+
end = idList.size();
717+
} else {
718+
end = (int) Math.min((long) idList.size(),
719+
this.offset + batchSize);
720+
}
721+
Set<Id> batchIds = InsertionOrderUtil.newSet();
722+
batchIds.addAll(idList.subList(this.offset, end));
723+
this.offset = end;
724+
return new PageIds(batchIds, PageState.EMPTY);
725+
}
726+
727+
@Override
728+
public Set<Id> all() {
729+
Set<Id> allIds = InsertionOrderUtil.newSet();
730+
allIds.addAll(idList);
731+
return allIds;
732+
}
733+
734+
@Override
735+
public void close() {
736+
this.offset = idList.size();
737+
}
738+
};
739+
}
740+
741+
private Set<Id> querySortedRangeIndexIds(IndexLabel indexLabel,
742+
ConditionQuery query) {
743+
List<HugeIndex> indexes = this.querySortedRangeIndexes(indexLabel,
744+
query);
745+
Set<Id> ids = InsertionOrderUtil.newSet();
746+
for (HugeIndex index : indexes) {
747+
ids.addAll(index.elementIds());
748+
Query.checkForceCapacity(ids.size());
749+
}
750+
return ids;
751+
}
752+
753+
private PageIds querySortedRangeIndexPage(IndexLabel indexLabel,
754+
ConditionQuery query) {
755+
List<HugeIndex> indexes = this.querySortedRangeIndexes(indexLabel,
756+
query);
757+
Set<Id> allIds = InsertionOrderUtil.newSet();
758+
for (HugeIndex index : indexes) {
759+
allIds.addAll(index.elementIds());
760+
Query.checkForceCapacity(allIds.size());
761+
}
762+
if (allIds.isEmpty()) {
763+
return PageIds.EMPTY;
764+
}
765+
766+
int start = 0;
767+
if (!query.page().isEmpty()) {
768+
start = PageState.fromString(query.page()).offset();
769+
}
770+
if (start >= allIds.size()) {
771+
return PageIds.EMPTY;
772+
}
773+
774+
long total = allIds.size();
775+
long end = query.noLimit() ? total :
776+
Math.min(total, (long) start + query.limit());
777+
Set<Id> pageIds = CollectionUtil.subSet(allIds, start, (int) end);
778+
if (pageIds.isEmpty()) {
779+
return PageIds.EMPTY;
780+
}
781+
782+
int next = (int) end;
783+
PageState pageState;
784+
if (next < total) {
785+
pageState = new PageState(new byte[]{1}, next, pageIds.size());
786+
} else {
787+
pageState = new PageState(PageState.EMPTY_BYTES, 0,
788+
pageIds.size());
789+
}
790+
return new PageIds(pageIds, pageState);
791+
}
792+
793+
private List<HugeIndex> querySortedRangeIndexes(IndexLabel indexLabel,
794+
ConditionQuery query) {
795+
List<HugeIndex> indexes = new ArrayList<>();
796+
Iterator<BackendEntry> entries = null;
797+
String spaceGraph = this.params()
798+
.graph().spaceGraphName();
799+
LockUtil.Locks locks = new LockUtil.Locks(spaceGraph);
800+
ConditionQuery scanQuery = query.copy();
801+
scanQuery.page(null);
802+
scanQuery.limit(Query.NO_LIMIT);
803+
try {
804+
locks.lockReads(LockUtil.INDEX_LABEL_DELETE, indexLabel.id());
805+
locks.lockReads(LockUtil.INDEX_LABEL_REBUILD, indexLabel.id());
806+
if (!indexLabel.system()) {
807+
graph().indexLabel(indexLabel.id());
808+
}
809+
810+
entries = super.query(scanQuery).iterator();
811+
while (entries.hasNext()) {
812+
HugeIndex index = this.readMatchedIndex(indexLabel, scanQuery,
813+
entries.next());
814+
if (index == null) {
815+
continue;
816+
}
817+
this.removeExpiredIndexIfNeeded(index, scanQuery.showExpired());
818+
this.recordIndexValue(scanQuery, index);
819+
indexes.add(index);
820+
Query.checkForceCapacity(indexes.size());
821+
}
822+
} finally {
823+
locks.unlock();
824+
CloseableIterator.closeIterator(entries);
825+
}
826+
827+
Collections.sort(indexes, (a, b) -> {
828+
return this.compareRangeIndexValues(a, b);
829+
});
830+
return indexes;
831+
}
832+
833+
@SuppressWarnings({"rawtypes", "unchecked"})
834+
private int compareRangeIndexValues(HugeIndex left, HugeIndex right) {
835+
Object leftValue = left.fieldValues();
836+
Object rightValue = right.fieldValues();
837+
E.checkArgument(leftValue instanceof Comparable,
838+
"Invalid range index value '%s'", leftValue);
839+
E.checkArgument(rightValue instanceof Comparable,
840+
"Invalid range index value '%s'", rightValue);
841+
return ((Comparable) leftValue).compareTo(rightValue);
842+
}
843+
663844
@Watched(prefix = "index")
664845
private IdHolder doIndexQueryBatch(IndexLabel indexLabel,
665846
ConditionQuery query) {
@@ -772,8 +953,8 @@ private HugeIndex readMatchedIndex(IndexLabel indexLabel,
772953
if (!missingIndexLabel(e)) {
773954
throw e;
774955
}
775-
LOG.debug("Skip stale index entry with missing index label while " +
776-
"querying index label '{}'", indexLabel.id(), e);
956+
LOG.debug("Skip stale index entry with missing index label " +
957+
"while querying index label '{}'", indexLabel.id(), e);
777958
return null;
778959
}
779960
if (!Objects.equals(index.indexLabelId(), indexLabel.id())) {

0 commit comments

Comments
 (0)