9
9
10
10
import org .apache .logging .log4j .LogManager ;
11
11
import org .apache .logging .log4j .Logger ;
12
+ import org .elasticsearch .cluster .metadata .IndexMetadata ;
12
13
import org .elasticsearch .cluster .metadata .Metadata ;
14
+ import org .elasticsearch .common .UUIDs ;
13
15
import org .elasticsearch .common .compress .CompressedXContent ;
14
16
import org .elasticsearch .common .settings .Settings ;
17
+ import org .elasticsearch .core .CheckedFunction ;
18
+ import org .elasticsearch .core .Strings ;
15
19
import org .elasticsearch .index .IndexMode ;
16
20
import org .elasticsearch .index .IndexSettingProvider ;
17
21
import org .elasticsearch .index .IndexSettings ;
22
+ import org .elasticsearch .index .IndexVersion ;
23
+ import org .elasticsearch .index .mapper .MapperService ;
18
24
25
+ import java .io .IOException ;
19
26
import java .time .Instant ;
20
27
import java .util .List ;
21
- import java .util .Locale ;
28
+
29
+ import static org .elasticsearch .cluster .metadata .IndexMetadata .INDEX_ROUTING_PATH ;
22
30
23
31
/**
24
32
* An index setting provider that overwrites the source mode from synthetic to stored if synthetic source isn't allowed to be used.
25
33
*/
26
- public class SyntheticSourceIndexSettingsProvider implements IndexSettingProvider {
34
+ final class SyntheticSourceIndexSettingsProvider implements IndexSettingProvider {
27
35
28
36
private static final Logger LOGGER = LogManager .getLogger (SyntheticSourceIndexSettingsProvider .class );
29
37
30
38
private final SyntheticSourceLicenseService syntheticSourceLicenseService ;
39
+ private final CheckedFunction <IndexMetadata , MapperService , IOException > mapperServiceFactory ;
31
40
32
- public SyntheticSourceIndexSettingsProvider (SyntheticSourceLicenseService syntheticSourceLicenseService ) {
41
+ SyntheticSourceIndexSettingsProvider (
42
+ SyntheticSourceLicenseService syntheticSourceLicenseService ,
43
+ CheckedFunction <IndexMetadata , MapperService , IOException > mapperServiceFactory
44
+ ) {
33
45
this .syntheticSourceLicenseService = syntheticSourceLicenseService ;
46
+ this .mapperServiceFactory = mapperServiceFactory ;
34
47
}
35
48
36
49
@ Override
@@ -46,19 +59,71 @@ public Settings getAdditionalIndexSettings(
46
59
// This index name is used when validating component and index templates, we should skip this check in that case.
47
60
// (See MetadataIndexTemplateService#validateIndexTemplateV2(...) method)
48
61
boolean isTemplateValidation = "validate-index-name" .equals (indexName );
49
- if (newIndexHasSyntheticSourceUsage (indexTemplateAndCreateRequestSettings )
62
+ if (newIndexHasSyntheticSourceUsage (indexName , isTimeSeries , indexTemplateAndCreateRequestSettings , combinedTemplateMappings )
50
63
&& syntheticSourceLicenseService .fallbackToStoredSource (isTemplateValidation )) {
51
64
LOGGER .debug ("creation of index [{}] with synthetic source without it being allowed" , indexName );
52
65
// TODO: handle falling back to stored source
53
66
}
54
67
return Settings .EMPTY ;
55
68
}
56
69
57
- boolean newIndexHasSyntheticSourceUsage (Settings indexTemplateAndCreateRequestSettings ) {
58
- // TODO: build tmp MapperService and check whether SourceFieldMapper#isSynthetic() to determine synthetic source usage.
59
- // Not using IndexSettings.MODE.get() to avoid validation that may fail at this point.
60
- var rawIndexMode = indexTemplateAndCreateRequestSettings .get (IndexSettings .MODE .getKey ());
61
- IndexMode indexMode = rawIndexMode != null ? Enum .valueOf (IndexMode .class , rawIndexMode .toUpperCase (Locale .ROOT )) : null ;
62
- return indexMode != null && indexMode .isSyntheticSourceEnabled ();
70
+ boolean newIndexHasSyntheticSourceUsage (
71
+ String indexName ,
72
+ boolean isTimeSeries ,
73
+ Settings indexTemplateAndCreateRequestSettings ,
74
+ List <CompressedXContent > combinedTemplateMappings
75
+ ) {
76
+ if ("validate-index-name" .equals (indexName )) {
77
+ // This index name is used when validating component and index templates, we should skip this check in that case.
78
+ // (See MetadataIndexTemplateService#validateIndexTemplateV2(...) method)
79
+ return false ;
80
+ }
81
+
82
+ var tmpIndexMetadata = buildIndexMetadataForMapperService (indexName , isTimeSeries , indexTemplateAndCreateRequestSettings );
83
+ try (var mapperService = mapperServiceFactory .apply (tmpIndexMetadata )) {
84
+ // combinedTemplateMappings can be null when creating system indices
85
+ // combinedTemplateMappings can be empty when creating a normal index that doesn't match any template and without mapping.
86
+ if (combinedTemplateMappings == null || combinedTemplateMappings .isEmpty ()) {
87
+ combinedTemplateMappings = List .of (new CompressedXContent ("{}" ));
88
+ }
89
+ mapperService .merge (MapperService .SINGLE_MAPPING_NAME , combinedTemplateMappings , MapperService .MergeReason .INDEX_TEMPLATE );
90
+ return mapperService .documentMapper ().sourceMapper ().isSynthetic ();
91
+ } catch (AssertionError | Exception e ) {
92
+ // In case invalid mappings or setting are provided, then mapper service creation can fail.
93
+ // In that case it is ok to return false here. The index creation will fail anyway later, so need to fallback to stored source.
94
+ LOGGER .info (() -> Strings .format ("unable to create mapper service for index [%s]" , indexName ), e );
95
+ return false ;
96
+ }
97
+ }
98
+
99
+ // Create a dummy IndexMetadata instance that can be used to create a MapperService in order to check whether synthetic source is used:
100
+ private IndexMetadata buildIndexMetadataForMapperService (
101
+ String indexName ,
102
+ boolean isTimeSeries ,
103
+ Settings indexTemplateAndCreateRequestSettings
104
+ ) {
105
+ var tmpIndexMetadata = IndexMetadata .builder (indexName );
106
+
107
+ int dummyPartitionSize = IndexMetadata .INDEX_ROUTING_PARTITION_SIZE_SETTING .get (indexTemplateAndCreateRequestSettings );
108
+ int dummyShards = indexTemplateAndCreateRequestSettings .getAsInt (
109
+ IndexMetadata .SETTING_NUMBER_OF_SHARDS ,
110
+ dummyPartitionSize == 1 ? 1 : dummyPartitionSize + 1
111
+ );
112
+ int shardReplicas = indexTemplateAndCreateRequestSettings .getAsInt (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , 0 );
113
+ var finalResolvedSettings = Settings .builder ()
114
+ .put (IndexMetadata .SETTING_VERSION_CREATED , IndexVersion .current ())
115
+ .put (indexTemplateAndCreateRequestSettings )
116
+ .put (IndexMetadata .SETTING_NUMBER_OF_SHARDS , dummyShards )
117
+ .put (IndexMetadata .SETTING_NUMBER_OF_REPLICAS , shardReplicas )
118
+ .put (IndexMetadata .SETTING_INDEX_UUID , UUIDs .randomBase64UUID ());
119
+
120
+ if (isTimeSeries ) {
121
+ finalResolvedSettings .put (IndexSettings .MODE .getKey (), IndexMode .TIME_SERIES );
122
+ // Avoid failing because index.routing_path is missing (in case fields are marked as dimension)
123
+ finalResolvedSettings .putList (INDEX_ROUTING_PATH .getKey (), List .of ("path" ));
124
+ }
125
+
126
+ tmpIndexMetadata .settings (finalResolvedSettings );
127
+ return tmpIndexMetadata .build ();
63
128
}
64
129
}
0 commit comments