Talk about deadlock analyzer of elastic search

order

This paper focuses on the deadlock analyzer of elastic search

DeadlockAnalyzer

elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/monitor/jvm/DeadlockAnalyzer.java

public class DeadlockAnalyzer {

    private static final Deadlock NULL_RESULT[] = new Deadlock[0];
    private final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

    private static DeadlockAnalyzer INSTANCE = new DeadlockAnalyzer();

    public static DeadlockAnalyzer deadlockAnalyzer() {
        return INSTANCE;
    }

    private DeadlockAnalyzer() {

    }

    public Deadlock[] findDeadlocks() {
        long deadlockedThreads[] = threadBean.findMonitorDeadlockedThreads();
        if (deadlockedThreads == null || deadlockedThreads.length == 0) {
            return NULL_RESULT;
        }
        Map<Long, ThreadInfo> threadInfoMap = createThreadInfoMap(deadlockedThreads);
        Set<LinkedHashSet<ThreadInfo>> cycles = calculateCycles(threadInfoMap);
        Set<LinkedHashSet<ThreadInfo>> chains = calculateCycleDeadlockChains(threadInfoMap, cycles);
        cycles.addAll(chains);
        return createDeadlockDescriptions(cycles);
    }


    private Deadlock[] createDeadlockDescriptions(Set<LinkedHashSet<ThreadInfo>> cycles) {
        Deadlock result[] = new Deadlock[cycles.size()];
        int count = 0;
        for (LinkedHashSet<ThreadInfo> cycle : cycles) {
            ThreadInfo asArray[] = cycle.toArray(new ThreadInfo[cycle.size()]);
            Deadlock d = new Deadlock(asArray);
            result[count++] = d;
        }
        return result;
    }


    private Set<LinkedHashSet<ThreadInfo>> calculateCycles(Map<Long, ThreadInfo> threadInfoMap) {
        Set<LinkedHashSet<ThreadInfo>> cycles = new HashSet<>();
        for (Map.Entry<Long, ThreadInfo> entry : threadInfoMap.entrySet()) {
            LinkedHashSet<ThreadInfo> cycle = new LinkedHashSet<>();
            for (ThreadInfo t = entry.getValue(); !cycle.contains(t); t = threadInfoMap.get(Long.valueOf(t.getLockOwnerId()))) {
                cycle.add(t);
            }

            if (!cycles.contains(cycle)) {
                cycles.add(cycle);
            }
        }
        return cycles;
    }


    private Set<LinkedHashSet<ThreadInfo>> calculateCycleDeadlockChains(Map<Long, ThreadInfo> threadInfoMap,
            Set<LinkedHashSet<ThreadInfo>> cycles) {
        ThreadInfo allThreads[] = threadBean.getThreadInfo(threadBean.getAllThreadIds());
        Set<LinkedHashSet<ThreadInfo>> deadlockChain = new HashSet<>();
        Set<Long> knownDeadlockedThreads = threadInfoMap.keySet();
        for (ThreadInfo threadInfo : allThreads) {
            Thread.State state = threadInfo.getThreadState();
            if (state == Thread.State.BLOCKED && !knownDeadlockedThreads.contains(threadInfo.getThreadId())) {
                for (LinkedHashSet<ThreadInfo> cycle : cycles) {
                    if (cycle.contains(threadInfoMap.get(Long.valueOf(threadInfo.getLockOwnerId())))) {
                        LinkedHashSet<ThreadInfo> chain = new LinkedHashSet<>();
                        ThreadInfo node = threadInfo;
                        while (!chain.contains(node)) {
                            chain.add(node);
                            node = threadInfoMap.get(Long.valueOf(node.getLockOwnerId()));
                        }
                        deadlockChain.add(chain);
                    }
                }

            }
        }

        return deadlockChain;
    }


    private Map<Long, ThreadInfo> createThreadInfoMap(long threadIds[]) {
        ThreadInfo threadInfos[] = threadBean.getThreadInfo(threadIds);
        Map<Long, ThreadInfo> threadInfoMap = new HashMap<>();
        for (ThreadInfo threadInfo : threadInfos) {
            threadInfoMap.put(threadInfo.getThreadId(), threadInfo);
        }
        return unmodifiableMap(threadInfoMap);
    }

    //......
}
  • The DeadlockAnalyzer provides the findDeadlocks method to return the information of deadlock threads. The method obtains the array of deadlockedThreads through the findMonitorDeadlockedThreads method of ThreadMXBean. If the array is null or empty, it returns NULL_RESULT. Otherwise, it will be calculated down
  • The createThreadInfoMap method obtains the corresponding threadInfo information from threadIds from ThreadMXBean, and then assembles it into a map of threadId and threadInfo; the calculateCycles method traverses the map, and then builds cycles according to the getLockOwnerId() of threadInfo
  • The calculateCycleDeadlockChains method builds cycleDeadlockChains according to threadInfoMap and cycles, adds them to cycles, and finally builds the Deadlock array through the createDeadlockDescriptions method

Deadlock

elasticsearch-7.0.1/server/src/main/java/org/elasticsearch/monitor/jvm/DeadlockAnalyzer.java

    public static class Deadlock {
        private final ThreadInfo members[];
        private final String description;
        private final Set<Long> memberIds;

        public Deadlock(ThreadInfo[] members) {
            this.members = members;

            Set<Long> builder = new HashSet<>();
            StringBuilder sb = new StringBuilder();
            for (int x = 0; x < members.length; x++) {
                ThreadInfo ti = members[x];
                sb.append(ti.getThreadName());
                sb.append(" > ");
                if (x == members.length - 1) {
                    sb.append(ti.getLockOwnerName());
                }
                builder.add(ti.getThreadId());
            }
            this.description = sb.toString();
            this.memberIds = unmodifiableSet(builder);
        }

        public ThreadInfo[] members() {
            return members;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Deadlock deadlock = (Deadlock) o;

            if (memberIds != null ? !memberIds.equals(deadlock.memberIds) : deadlock.memberIds != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = members != null ? Arrays.hashCode(members) : 0;
            result = 31 * result + (description != null ? description.hashCode() : 0);
            result = 31 * result + (memberIds != null ? memberIds.hashCode() : 0);
            return result;
        }

        @Override
        public String toString() {
            return description;
        }
    }
  • Deadlock contains three attributes: members, description and memberIds. Its constructor will build description according to members

Summary

  • The DeadlockAnalyzer provides the findDeadlocks method to return the information of deadlock threads. The method obtains the array of deadlockedThreads through the findMonitorDeadlockedThreads method of ThreadMXBean. If the array is null or empty, it returns NULL_RESULT. Otherwise, it will be calculated down
  • The createThreadInfoMap method obtains the corresponding threadInfo information from threadIds from ThreadMXBean, and then assembles it into a map of threadId and threadInfo; the calculateCycles method traverses the map, and then builds cycles according to the getLockOwnerId() of threadInfo
  • The calculateCycleDeadlockChains method builds cycleDeadlockChains according to threadInfoMap and cycles, adds them to cycles, and finally builds the Deadlock array through the createDeadlockDescriptions method

doc

Tags: ElasticSearch Java jvm

Posted on Tue, 05 Nov 2019 13:37:45 -0500 by cbn_noodles