
com.hazelcast.sql.impl.CoreQueryUtils Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.sql.impl;
import com.hazelcast.cluster.Member;
import com.hazelcast.jet.RestartableException;
import com.hazelcast.jet.impl.util.ExceptionUtil;
import com.hazelcast.sql.HazelcastSqlException;
import com.hazelcast.version.Version;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
public final class CoreQueryUtils {
private CoreQueryUtils() {
}
public static HazelcastSqlException toPublicException(@Nonnull Throwable e, @Nonnull UUID localMemberId) {
if (e instanceof HazelcastSqlException exception) {
return exception;
}
if (e instanceof QueryException e0) {
UUID originatingMemberId = e0.getOriginatingMemberId();
if (originatingMemberId == null) {
originatingMemberId = localMemberId;
}
return new HazelcastSqlException(originatingMemberId, e0.getCode(), e0.getMessage(), e, e0.getSuggestion());
} else {
Throwable copy = e;
while (copy != null) {
if (ExceptionUtil.isTopologyException(copy)) {
return new HazelcastSqlException(localMemberId, SqlErrorCode.TOPOLOGY_CHANGE, e.getMessage(), e, null);
} else if (copy instanceof RestartableException) {
return new HazelcastSqlException(localMemberId, SqlErrorCode.RESTARTABLE_ERROR, e.getMessage(), e, null);
}
copy = copy.getCause();
}
return new HazelcastSqlException(localMemberId, SqlErrorCode.GENERIC, e.getMessage(), e, null);
}
}
/**
* Finds a larger same-version group of data members from a collection of
* members and, if {@code localMember} is from that group, return that.
* Otherwise return a random member from the group. If the same-version
* groups have the same size, return a member from the newer group
* (preferably the local one).
*
* Used for SqlExecute and SubmitJob(light=true) messages.
*
* @param members list of all members
* @param localMember the local member, null for client instance
* @return the chosen member or null, if no data member is found
*/
@Nullable
@SuppressWarnings({"checkstyle:CyclomaticComplexity", "checkstyle:NPathComplexity"})
public static Member memberOfLargerSameVersionGroup(@Nonnull Collection members, @Nullable Member localMember) {
// The members should have at most 2 different version (ignoring the patch version).
// Find a random member from the larger same-version group.
// we don't use 2-element array to save on GC litter
Version version0 = null;
Version version1 = null;
int count0 = 0;
int count1 = 0;
int grossMajority = members.size() / 2;
for (Member m : members) {
if (m.isLiteMember()) {
continue;
}
Version v = m.getVersion().asVersion();
int currentCount;
if (version0 == null || version0.equals(v)) {
version0 = v;
currentCount = ++count0;
} else if (version1 == null || version1.equals(v)) {
version1 = v;
currentCount = ++count1;
} else {
throw new RuntimeException("More than 2 distinct member versions found: " + version0 + ", " + version1 + ", "
+ v);
}
// a shortcut
if (currentCount > grossMajority && localMember != null && localMember.getVersion().asVersion().equals(v)) {
return localMember;
}
}
assert count1 == 0 || count0 > 0;
// no data members
if (count0 == 0) {
return null;
}
int count;
Version version;
if (count0 > count1 || count0 == count1 && version0.compareTo(version1) > 0) {
count = count0;
version = version0;
} else {
count = count1;
version = version1;
}
// if the local member is data member and is from the larger group, use that
if (localMember != null && !localMember.isLiteMember()
&& localMember.getVersion().asVersion().equals(version)) {
return localMember;
}
// otherwise return a random member from the larger group
int randomMemberIndex = ThreadLocalRandom.current().nextInt(count);
for (Member m : members) {
if (!m.isLiteMember() && m.getVersion().asVersion().equals(version)) {
randomMemberIndex--;
if (randomMemberIndex < 0) {
return m;
}
}
}
throw new RuntimeException("should never get here");
}
}