Skip to content

Commit 51fcb8f

Browse files
committed
Add AOT example
1 parent 7bf34bc commit 51fcb8f

File tree

9 files changed

+183
-11
lines changed

9 files changed

+183
-11
lines changed

Elasticsearch.sln

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.ClusterLauncher", "te
5757
EndProject
5858
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "tests\Tests\Tests.csproj", "{6FD804B2-CE80-41CB-A411-2023F34C18FE}"
5959
EndProject
60+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "aot", "examples\aot\aot.csproj", "{3FA9C99A-7DA0-4DF2-89C0-BDDFC97E2CB7}"
61+
EndProject
62+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
63+
EndProject
6064
Global
6165
GlobalSection(SolutionConfigurationPlatforms) = preSolution
6266
Debug|Any CPU = Debug|Any CPU
@@ -103,6 +107,10 @@ Global
103107
{6FD804B2-CE80-41CB-A411-2023F34C18FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
104108
{6FD804B2-CE80-41CB-A411-2023F34C18FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
105109
{6FD804B2-CE80-41CB-A411-2023F34C18FE}.Release|Any CPU.Build.0 = Release|Any CPU
110+
{3FA9C99A-7DA0-4DF2-89C0-BDDFC97E2CB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
111+
{3FA9C99A-7DA0-4DF2-89C0-BDDFC97E2CB7}.Debug|Any CPU.Build.0 = Debug|Any CPU
112+
{3FA9C99A-7DA0-4DF2-89C0-BDDFC97E2CB7}.Release|Any CPU.ActiveCfg = Release|Any CPU
113+
{3FA9C99A-7DA0-4DF2-89C0-BDDFC97E2CB7}.Release|Any CPU.Build.0 = Release|Any CPU
106114
EndGlobalSection
107115
GlobalSection(SolutionProperties) = preSolution
108116
HideSolutionNode = FALSE
@@ -118,6 +126,7 @@ Global
118126
{68D1BFDC-F447-4D2C-AF81-537807636610} = {1FE49D14-216A-41EE-A177-E42BFF53E0DC}
119127
{F6162603-D134-4121-8106-2BA4DAD7350B} = {362B2776-4B29-46AB-B237-56776B5372B6}
120128
{6FD804B2-CE80-41CB-A411-2023F34C18FE} = {362B2776-4B29-46AB-B237-56776B5372B6}
129+
{3FA9C99A-7DA0-4DF2-89C0-BDDFC97E2CB7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
121130
EndGlobalSection
122131
GlobalSection(ExtensibilityGlobals) = postSolution
123132
SolutionGuid = {CE74F821-B001-4C69-A58D-CF81F8B0B632}

examples/aot/Program.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.Text.Json.Serialization;
4+
5+
using Elastic.Clients.Elasticsearch;
6+
using Elastic.Clients.Elasticsearch.Serialization;
7+
using Elastic.Transport;
8+
using Elastic.Transport.Extensions;
9+
10+
namespace AOT;
11+
12+
public static class Program
13+
{
14+
public static void Main(string[] args)
15+
{
16+
var nodePool = new SingleNodePool(new Uri("http://localhost:9200"));
17+
var settings = new ElasticsearchClientSettings(
18+
nodePool,
19+
sourceSerializer: (_, settings) =>
20+
new DefaultSourceSerializer(settings, UserTypeSerializerContext.Default)
21+
)
22+
.DefaultMappingFor<Person>(x => x.IndexName("index"));
23+
24+
var client = new ElasticsearchClient(settings);
25+
26+
var person = new Person
27+
{
28+
Id = 1234,
29+
FirstName = "Florian",
30+
LastName = "Bernd"
31+
};
32+
33+
Trace.Assert(client.Infer.Id(person) == "1234");
34+
35+
var indexRequest = new IndexRequest<Person>(person);
36+
var indexRequestBody = client.ElasticsearchClientSettings.RequestResponseSerializer.SerializeToString(indexRequest);
37+
var indexRequest2 = client.ElasticsearchClientSettings.RequestResponseSerializer.Deserialize<IndexRequest<Person>>(indexRequestBody)!;
38+
39+
Trace.Assert(indexRequest.Document == indexRequest2.Document);
40+
}
41+
}
42+
43+
internal sealed record Person
44+
{
45+
public long? Id { get; init; }
46+
public required string FirstName { get; init; }
47+
public required string LastName { get; init; }
48+
public DateTimeOffset? BirthDate { get; init; }
49+
}
50+
51+
[JsonSerializable(typeof(Person), GenerationMode = JsonSourceGenerationMode.Default)]
52+
internal sealed partial class UserTypeSerializerContext :
53+
JsonSerializerContext
54+
{
55+
}

examples/aot/aot.csproj

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
7+
<Nullable>enable</Nullable>
8+
<PublishAot>true</PublishAot>
9+
<InvariantGlobalization>true</InvariantGlobalization>
10+
<TrimmerSingleWarn>false</TrimmerSingleWarn>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\..\src\Elastic.Clients.Elasticsearch\Elastic.Clients.Elasticsearch.csproj" />
15+
</ItemGroup>
16+
17+
</Project>

src/Elastic.Clients.Elasticsearch/Elastic.Clients.Elasticsearch.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<PropertyGroup>
1515
<IsPackable>true</IsPackable>
1616
<GenerateDocumentationFile>true</GenerateDocumentationFile>
17-
<TargetFrameworks>netstandard2.0;net462;netstandard2.1;net8.0</TargetFrameworks>
17+
<TargetFrameworks>netstandard2.0;net462;netstandard2.1;net8.0;net9.0</TargetFrameworks>
1818
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
1919
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
2020
<Nullable>annotations</Nullable>

src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/DynamicPropertyAccessor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ internal static class DynamicPropertyAccessor
5050

5151
// Build compiled getter delegate.
5252

53-
#pragma warning disable IL3050
53+
#pragma warning disable IL3050, IL2060
5454
var getterDelegateFactory = MakeDelegateMethodInfo.MakeGenericMethod(type, getterMethod.ReturnType);
55-
#pragma warning restore IL3050
55+
#pragma warning restore IL3050, IL2060
5656
var genericGetterDelegate = (Func<object, object>)getterDelegateFactory.Invoke(null, [getterMethod])!;
5757

5858
return instance => retrieverFunc(genericGetterDelegate, instance);

src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/Id/IdResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public IdResolver(IElasticsearchClientSettings settings)
2828
return null;
2929
}
3030

31-
return Resolve(instance.GetType(), instance);
31+
return Resolve(typeof(T), instance);
3232
}
3333

3434
public string? Resolve([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, object instance)

src/Elastic.Clients.Elasticsearch/_Shared/Core/Infer/RoutingResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public RoutingResolver(IElasticsearchClientSettings settings, IdResolver idResol
3030
return null;
3131
}
3232

33-
return Resolve(instance.GetType(), instance);
33+
return Resolve(typeof(T), instance);
3434
}
3535

3636
public string? Resolve([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, object instance)

src/Playground/Person.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System.Runtime.Serialization;
66
using Elastic.Clients.Elasticsearch;
7+
using Elastic.Clients.Elasticsearch.QueryDsl;
78

89
namespace Playground
910
{
@@ -30,6 +31,8 @@ public class Person
3031
public string Data { get; init; } = "NOTHING";
3132

3233
public DateTimeKind Enum { get; init; }
34+
35+
public Query? Q { get; init; }
3336
}
3437

3538
public class PersonV3

src/Playground/Program.cs

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,107 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information.
44

5+
using System.Runtime.CompilerServices;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization;
58
using Elastic.Clients.Elasticsearch;
9+
using Elastic.Clients.Elasticsearch.Aggregations;
10+
using Elastic.Clients.Elasticsearch.Nodes;
11+
using Elastic.Clients.Elasticsearch.QueryDsl;
12+
using Elastic.Clients.Elasticsearch.Requests;
13+
using Elastic.Clients.Elasticsearch.Serialization;
614
using Elastic.Transport;
7-
15+
using Elastic.Transport.Extensions;
816
using Playground;
917

18+
RequestConfiguration? globalRequestConfiguration = null;
19+
ConditionalWeakTable<RequestConfiguration, RequestConfiguration>? globalRequestConfigurations = null;
20+
1021
var settings = new ElasticsearchClientSettings(new Uri("https://primary.es.europe-west3.gcp.cloud.es.io"))
1122
.Authentication(new BasicAuthentication("elastic", "Oov35Wtxj5DzpZNzYAzFb0KZ"))
12-
.DisableDirectStreaming()
13-
.EnableDebugMode(cd =>
23+
.OnBeforeRequest(OnBeforeRequest)
24+
.EnableDebugMode(cd => Console.WriteLine(cd.DebugInformation));
25+
void OnBeforeRequest(ElasticsearchClient client, Request request, EndpointPath endpointPath, ref PostData? postData, ref IRequestConfiguration? requestConfiguration)
26+
{
27+
// Each time a request is made, the transport creates a new `BoundConfiguration` for every `IRequestConfiguration`
28+
// that is not in the cache (based on reference equality).
29+
30+
// To prevent frequent allocations of our mutated request configurations (and the secondary allocations for
31+
// `BoundConfiguration`), we have to maintain a custom cache that maps every original request configuration to the
32+
// mutated one.
33+
34+
if (requestConfiguration is null)
35+
{
36+
globalRequestConfiguration = Interlocked.CompareExchange(
37+
ref globalRequestConfiguration,
38+
new RequestConfiguration
39+
{
40+
UserAgent = UserAgent.Create("my-custom-user-agent")
41+
},
42+
null) ?? globalRequestConfiguration;
43+
44+
requestConfiguration = globalRequestConfiguration;
45+
return;
46+
}
47+
48+
if (requestConfiguration is not RequestConfiguration rc)
49+
{
50+
// Only `RequestConfiguration` (not all implementations of `IRequestConfiguration`) gets cached in the
51+
// internal cache.
52+
requestConfiguration = MutateRequestConfiguration(requestConfiguration);
53+
return;
54+
}
55+
56+
// ReSharper disable InconsistentlySynchronizedField
57+
58+
var cache = (Interlocked.CompareExchange(
59+
ref globalRequestConfigurations,
60+
new ConditionalWeakTable<RequestConfiguration, RequestConfiguration>(),
61+
null
62+
) ?? globalRequestConfigurations);
63+
64+
if (cache.TryGetValue(rc, out var mutatedRequestConfiguration))
1465
{
15-
//var request = System.Text.Encoding.Default.GetString(cd.RequestBodyInBytes);
16-
Console.WriteLine(cd.DebugInformation);
17-
});
66+
requestConfiguration = mutatedRequestConfiguration;
67+
return;
68+
}
69+
70+
mutatedRequestConfiguration = MutateRequestConfiguration(rc);
71+
72+
#if NET8_0_OR_GREATER
73+
cache.TryAdd(rc, mutatedRequestConfiguration);
74+
#else
75+
lock (cache)
76+
{
77+
cache.Add(rc, mutatedRequestConfiguration);
78+
}
79+
#endif
80+
81+
// ReSharper restore InconsistentlySynchronizedField
82+
83+
return;
84+
85+
RequestConfiguration MutateRequestConfiguration(IRequestConfiguration requestConfiguration)
86+
{
87+
return new RequestConfiguration(requestConfiguration)
88+
{
89+
UserAgent = UserAgent.Create("my-custom-user-agent")
90+
};
91+
}
92+
}
1893

1994
var client = new ElasticsearchClient(settings);
2095

96+
var s = settings.SourceSerializer.SerializeToString(new Person
97+
{
98+
Q = new Query
99+
{
100+
MatchAll = new MatchAllQuery()
101+
}
102+
});
103+
104+
await client.ExplainAsync("my-tweet-index", 1);
105+
await client.DeleteAsync("my-tweet-index", 1);
106+
107+
BulkRequestDescriptor descriptor = new();
108+
descriptor.Index("indexName");

0 commit comments

Comments
 (0)