|
92 | 92 | import java.util.HashMap; |
93 | 93 | import java.util.HashSet; |
94 | 94 | import java.util.Iterator; |
| 95 | +import java.util.LinkedHashMap; |
95 | 96 | import java.util.List; |
96 | 97 | import java.util.Map; |
97 | 98 | import java.util.Map.Entry; |
@@ -2880,18 +2881,199 @@ public void insertRelationalTablets(List<Tablet> tablets) |
2880 | 2881 | tablets = mergeRelationalTablets(tablets); |
2881 | 2882 | mergeTabletsCost = System.nanoTime() - mergeTabletsStartTime; |
2882 | 2883 | } |
2883 | | - TSInsertTabletsReq request = genTSInsertTabletsReq(tablets, false, false); |
2884 | | - request.setWriteToTable(true); |
2885 | | - for (Tablet tablet : tablets) { |
2886 | | - request.addToColumnCategoriesList(toEnumOrdinalsAsBytes(tablet.getColumnTypes())); |
| 2884 | + if (tablets.isEmpty()) { |
| 2885 | + throw new BatchExecutionException(SessionMessages.NO_TABLET_INSERTING); |
2887 | 2886 | } |
2888 | 2887 | final long insertTabletsStartTime = System.nanoTime(); |
| 2888 | + if (enableRedirection) { |
| 2889 | + insertRelationalTabletsWithLeaderCache(tablets); |
| 2890 | + } else { |
| 2891 | + TSInsertTabletsReq request = genTSInsertTabletsReq(tablets, false, false); |
| 2892 | + request.setWriteToTable(true); |
| 2893 | + for (Tablet tablet : tablets) { |
| 2894 | + request.addToColumnCategoriesList(toEnumOrdinalsAsBytes(tablet.getColumnTypes())); |
| 2895 | + } |
| 2896 | + try { |
| 2897 | + getDefaultSessionConnection().insertTablets(request); |
| 2898 | + } catch (RedirectException ignored) { |
| 2899 | + } |
| 2900 | + } |
| 2901 | + if (recordMergeTabletsCost) { |
| 2902 | + recordMergeTabletsCost(mergeTabletsCost, System.nanoTime() - insertTabletsStartTime); |
| 2903 | + } |
| 2904 | + } |
| 2905 | + |
| 2906 | + private void insertRelationalTabletsWithLeaderCache(final List<Tablet> tablets) |
| 2907 | + throws IoTDBConnectionException, StatementExecutionException { |
| 2908 | + final Map<SessionConnection, RelationalTabletsReq> tabletGroup = new HashMap<>(); |
| 2909 | + for (final Tablet tablet : tablets) { |
| 2910 | + addRelationalTabletToGroup(tabletGroup, tablet); |
| 2911 | + } |
| 2912 | + if (tabletGroup.size() == 1) { |
| 2913 | + insertRelationalTabletsOnce(tabletGroup); |
| 2914 | + } else { |
| 2915 | + insertRelationalTabletsByGroup(tabletGroup); |
| 2916 | + } |
| 2917 | + } |
| 2918 | + |
| 2919 | + private void addRelationalTabletToGroup( |
| 2920 | + final Map<SessionConnection, RelationalTabletsReq> tabletGroup, final Tablet tablet) |
| 2921 | + throws IoTDBConnectionException { |
| 2922 | + if (SessionUtils.isTabletContainsSingleDevice(tablet)) { |
| 2923 | + final SessionConnection connection = getTableModelSessionConnection(tablet.getDeviceID(0)); |
| 2924 | + updateRelationalTabletsReq(tabletGroup, connection, tablet); |
| 2925 | + return; |
| 2926 | + } |
| 2927 | + for (int row = 0; row < tablet.getRowSize(); row++) { |
| 2928 | + final IDeviceID deviceID = tablet.getDeviceID(row); |
| 2929 | + final SessionConnection connection = getTableModelSessionConnection(deviceID); |
| 2930 | + final Tablet subTablet = |
| 2931 | + tabletGroup |
| 2932 | + .computeIfAbsent(connection, ignored -> new RelationalTabletsReq()) |
| 2933 | + .tabletByDevice |
| 2934 | + .computeIfAbsent(deviceID, ignored -> createEmptyRelationalTabletLike(tablet)); |
| 2935 | + for (int column = 0; column < subTablet.getSchemas().size(); column++) { |
| 2936 | + subTablet.addValue( |
| 2937 | + subTablet.getSchemas().get(column).getMeasurementName(), |
| 2938 | + subTablet.getRowSize(), |
| 2939 | + tablet.getValue(row, column)); |
| 2940 | + } |
| 2941 | + subTablet.addTimestamp(subTablet.getRowSize(), tablet.getTimestamp(row)); |
| 2942 | + } |
| 2943 | + } |
| 2944 | + |
| 2945 | + private SessionConnection getTableModelSessionConnection(final IDeviceID deviceID) |
| 2946 | + throws IoTDBConnectionException { |
| 2947 | + return tableModelDeviceIdToEndpoint == null || tableModelDeviceIdToEndpoint.isEmpty() |
| 2948 | + ? getDefaultSessionConnection() |
| 2949 | + : getSessionConnection(deviceID); |
| 2950 | + } |
| 2951 | + |
| 2952 | + private Tablet createEmptyRelationalTabletLike(final Tablet tablet) { |
| 2953 | + final List<String> measurements = new ArrayList<>(tablet.getSchemas().size()); |
| 2954 | + final List<TSDataType> dataTypes = new ArrayList<>(tablet.getSchemas().size()); |
| 2955 | + tablet |
| 2956 | + .getSchemas() |
| 2957 | + .forEach( |
| 2958 | + schema -> { |
| 2959 | + measurements.add(schema.getMeasurementName()); |
| 2960 | + dataTypes.add(schema.getType()); |
| 2961 | + }); |
| 2962 | + return new Tablet( |
| 2963 | + tablet.getTableName(), |
| 2964 | + measurements, |
| 2965 | + dataTypes, |
| 2966 | + tablet.getColumnTypes(), |
| 2967 | + tablet.getRowSize()); |
| 2968 | + } |
| 2969 | + |
| 2970 | + private void updateRelationalTabletsReq( |
| 2971 | + final Map<SessionConnection, RelationalTabletsReq> tabletGroup, |
| 2972 | + final SessionConnection connection, |
| 2973 | + final Tablet tablet) { |
| 2974 | + tabletGroup |
| 2975 | + .computeIfAbsent(connection, ignored -> new RelationalTabletsReq()) |
| 2976 | + .addTablet(tablet); |
| 2977 | + } |
| 2978 | + |
| 2979 | + private void buildRelationalTabletsRequests( |
| 2980 | + final Map<SessionConnection, RelationalTabletsReq> tabletGroup) { |
| 2981 | + for (final RelationalTabletsReq relationalTabletsReq : tabletGroup.values()) { |
| 2982 | + relationalTabletsReq.buildRequest(); |
| 2983 | + } |
| 2984 | + } |
| 2985 | + |
| 2986 | + private void insertRelationalTabletsOnce( |
| 2987 | + final Map<SessionConnection, RelationalTabletsReq> tabletGroup) |
| 2988 | + throws IoTDBConnectionException, StatementExecutionException { |
| 2989 | + buildRelationalTabletsRequests(tabletGroup); |
| 2990 | + final Map.Entry<SessionConnection, RelationalTabletsReq> entry = |
| 2991 | + tabletGroup.entrySet().iterator().next(); |
| 2992 | + final SessionConnection connection = entry.getKey(); |
| 2993 | + final RelationalTabletsReq relationalTabletsReq = entry.getValue(); |
2889 | 2994 | try { |
2890 | | - getDefaultSessionConnection().insertTablets(request); |
2891 | | - } catch (RedirectException ignored) { |
2892 | | - } finally { |
2893 | | - if (recordMergeTabletsCost) { |
2894 | | - recordMergeTabletsCost(mergeTabletsCost, System.nanoTime() - insertTabletsStartTime); |
| 2995 | + connection.insertTablets(relationalTabletsReq.request); |
| 2996 | + } catch (RedirectException e) { |
| 2997 | + handleRelationalTabletsRedirection(e, relationalTabletsReq.deviceIDs); |
| 2998 | + } catch (IoTDBConnectionException e) { |
| 2999 | + if (endPointToSessionConnection != null && endPointToSessionConnection.size() > 1) { |
| 3000 | + removeBrokenSessionConnection(connection); |
| 3001 | + try { |
| 3002 | + getDefaultSessionConnection().insertTablets(relationalTabletsReq.request); |
| 3003 | + } catch (RedirectException ignored) { |
| 3004 | + } |
| 3005 | + } else { |
| 3006 | + throw e; |
| 3007 | + } |
| 3008 | + } |
| 3009 | + } |
| 3010 | + |
| 3011 | + @SuppressWarnings({ |
| 3012 | + "squid:S3776" |
| 3013 | + }) // ignore Cognitive Complexity of methods should not be too high |
| 3014 | + private void insertRelationalTabletsByGroup( |
| 3015 | + final Map<SessionConnection, RelationalTabletsReq> tabletGroup) |
| 3016 | + throws IoTDBConnectionException, StatementExecutionException { |
| 3017 | + buildRelationalTabletsRequests(tabletGroup); |
| 3018 | + final List<CompletableFuture<Void>> completableFutures = |
| 3019 | + tabletGroup.entrySet().stream() |
| 3020 | + .map( |
| 3021 | + entry -> { |
| 3022 | + final SessionConnection connection = entry.getKey(); |
| 3023 | + final RelationalTabletsReq relationalTabletsReq = entry.getValue(); |
| 3024 | + return CompletableFuture.runAsync( |
| 3025 | + () -> { |
| 3026 | + try { |
| 3027 | + connection.insertTablets(relationalTabletsReq.request); |
| 3028 | + } catch (RedirectException e) { |
| 3029 | + handleRelationalTabletsRedirection(e, relationalTabletsReq.deviceIDs); |
| 3030 | + } catch (StatementExecutionException e) { |
| 3031 | + throw new CompletionException(e); |
| 3032 | + } catch (IoTDBConnectionException e) { |
| 3033 | + removeBrokenSessionConnection(connection); |
| 3034 | + try { |
| 3035 | + getDefaultSessionConnection() |
| 3036 | + .insertTablets(relationalTabletsReq.request); |
| 3037 | + } catch (IoTDBConnectionException | StatementExecutionException ex) { |
| 3038 | + throw new CompletionException(ex); |
| 3039 | + } catch (RedirectException ignored) { |
| 3040 | + } |
| 3041 | + } |
| 3042 | + }, |
| 3043 | + OPERATION_EXECUTOR); |
| 3044 | + }) |
| 3045 | + .collect(Collectors.toList()); |
| 3046 | + |
| 3047 | + final StringBuilder errMsgBuilder = new StringBuilder(); |
| 3048 | + for (final CompletableFuture<Void> completableFuture : completableFutures) { |
| 3049 | + try { |
| 3050 | + completableFuture.join(); |
| 3051 | + } catch (CompletionException completionException) { |
| 3052 | + final Throwable cause = completionException.getCause(); |
| 3053 | + logger.error(SessionMessages.MEET_ERROR_WHEN_ASYNC_INSERT, cause); |
| 3054 | + if (cause instanceof IoTDBConnectionException) { |
| 3055 | + throw (IoTDBConnectionException) cause; |
| 3056 | + } |
| 3057 | + if (errMsgBuilder.length() > 0) { |
| 3058 | + errMsgBuilder.append(";"); |
| 3059 | + } |
| 3060 | + errMsgBuilder.append(cause.getMessage()); |
| 3061 | + } |
| 3062 | + } |
| 3063 | + if (errMsgBuilder.length() > 0) { |
| 3064 | + throw new StatementExecutionException(errMsgBuilder.toString()); |
| 3065 | + } |
| 3066 | + } |
| 3067 | + |
| 3068 | + private void handleRelationalTabletsRedirection( |
| 3069 | + final RedirectException redirectException, final List<IDeviceID> deviceIDs) { |
| 3070 | + final List<TEndPoint> endPointList = redirectException.getEndPointList(); |
| 3071 | + if (endPointList == null) { |
| 3072 | + return; |
| 3073 | + } |
| 3074 | + for (int i = 0; i < endPointList.size() && i < deviceIDs.size(); i++) { |
| 3075 | + if (endPointList.get(i) != null) { |
| 3076 | + handleRedirection(deviceIDs.get(i), endPointList.get(i)); |
2895 | 3077 | } |
2896 | 3078 | } |
2897 | 3079 | } |
@@ -3125,6 +3307,30 @@ private RelationalTabletWithColumnIndexMap( |
3125 | 3307 | } |
3126 | 3308 | } |
3127 | 3309 |
|
| 3310 | + private class RelationalTabletsReq { |
| 3311 | + |
| 3312 | + private final TSInsertTabletsReq request = new TSInsertTabletsReq(); |
| 3313 | + private final List<IDeviceID> deviceIDs = new ArrayList<>(); |
| 3314 | + private final List<Tablet> tablets = new ArrayList<>(); |
| 3315 | + private final Map<IDeviceID, Tablet> tabletByDevice = new LinkedHashMap<>(); |
| 3316 | + |
| 3317 | + private void addTablet(final Tablet tablet) { |
| 3318 | + tablets.add(tablet); |
| 3319 | + } |
| 3320 | + |
| 3321 | + private void buildRequest() { |
| 3322 | + request.setWriteToTable(true); |
| 3323 | + tablets.forEach(this::addTabletToRequest); |
| 3324 | + tabletByDevice.values().forEach(this::addTabletToRequest); |
| 3325 | + } |
| 3326 | + |
| 3327 | + private void addTabletToRequest(final Tablet tablet) { |
| 3328 | + updateTSInsertTabletsReq(request, tablet, false, false); |
| 3329 | + request.addToColumnCategoriesList(toEnumOrdinalsAsBytes(tablet.getColumnTypes())); |
| 3330 | + deviceIDs.add(tablet.getDeviceID(0)); |
| 3331 | + } |
| 3332 | + } |
| 3333 | + |
3128 | 3334 | private void insertRelationalTabletWithLeaderCache(Tablet tablet) |
3129 | 3335 | throws IoTDBConnectionException, StatementExecutionException { |
3130 | 3336 | Map<SessionConnection, Tablet> relationalTabletGroup = new HashMap<>(); |
|
0 commit comments