Skip to content

Commit a24c503

Browse files
committed
Merge branch '6.0' into 7.0
# Conflicts: # Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Extractor.cs # Orm/Xtensive.Orm/Orm/StorageNodeManager.cs # Version.props
2 parents 1fc7ed5 + 27da6cc commit a24c503

File tree

10 files changed

+325
-10
lines changed

10 files changed

+325
-10
lines changed

ChangeLog/6.0.11_Z_Final.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[main] Added an option to remove cached queries of removing storage node along with the node, default behavior remains the same
2+
[postgresql] Dedicated exception when the extracting schema doesn't exist or it belongs to another user
3+
[weaver] Fixed constructor processing with try...catch, catch section now has correct leave

Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/Resources/Strings.Designer.cs

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/Resources/Strings.resx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,10 @@
112112
<value>2.0</value>
113113
</resheader>
114114
<resheader name="reader">
115-
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
115+
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
116116
</resheader>
117117
<resheader name="writer">
118-
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
118+
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120120
<data name="ExPostgreSqlBelow83IsNotSupported" xml:space="preserve">
121121
<value>PostgreSQL below 8.3 is not supported.</value>
@@ -132,4 +132,7 @@
132132
<data name="ExTheTypeOfGivenParameterCannotBeTreatedAsOffsetForDateTimeOffsetConstruction" xml:space="preserve">
133133
<value>The type of given parameter cannot be treated as offset for DateTimeOffset construction.</value>
134134
</data>
135+
<data name="ExSchemaXDoesNotExistOrBelongsToAnotherUser" xml:space="preserve">
136+
<value>Schema '{0}' either does not exist or belongs to another user.</value>
137+
</data>
135138
</root>

Orm/Xtensive.Orm.PostgreSql/Sql.Drivers.PostgreSql/v8_0/Extractor.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,11 @@ protected virtual ISqlCompileUnit BuildExtractSchemaContentsQuery(ExtractionCont
580580
var targetSchemes = context.TargetSchemes;
581581
if (targetSchemes != null && targetSchemes.Count > 0) {
582582
var schemesIndexes = catalog.Schemas.Where(sch => targetSchemes.ContainsKey(sch.Name))
583-
.Select(sch => context.ReversedSchemaMap[sch]);
583+
.Select(sch =>
584+
context.ReversedSchemaMap.TryGetValue(sch, out var oid)
585+
? oid
586+
: throw new InvalidOperationException(string.Format(Resources.Strings.ExSchemaXDoesNotExistOrBelongsToAnotherUser, sch.Name))
587+
);
584588
select.Where &= SqlDml.In(relationsTable["relnamespace"], CreateOidRow(schemesIndexes));
585589
}
586590

Orm/Xtensive.Orm.PostgreSql/Xtensive.Orm.PostgreSql.csproj

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<IsPackable>true</IsPackable>
44
<DocumentationFile>$(OutputPath)$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
@@ -31,4 +31,16 @@
3131
<Link>Properties\Visibility.cs</Link>
3232
</Compile>
3333
</ItemGroup>
34+
<ItemGroup>
35+
<None Include="Sql.Drivers.PostgreSql\Resources\Strings.resx">
36+
<Generator>ResXFileCodeGenerator</Generator>
37+
<SubType>Designer</SubType>
38+
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
39+
</None>
40+
<Compile Update="Sql.Drivers.PostgreSql\Resources\Strings.Designer.cs">
41+
<DesignTime>True</DesignTime>
42+
<AutoGen>True</AutoGen>
43+
<DependentUpon>Strings.resx</DependentUpon>
44+
</Compile>
45+
</ItemGroup>
3446
</Project>

Orm/Xtensive.Orm.Tests/Storage/Multinode/QueryCachingTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ protected override void CheckRequirements() =>
107107

108108
protected override DomainConfiguration BuildConfiguration()
109109
{
110+
CustomUpgradeHandler.TypeIdPerNode.Clear();
110111
CustomUpgradeHandler.TypeIdPerNode.Add(TestNodeId2, 200);
111112
CustomUpgradeHandler.TypeIdPerNode.Add(TestNodeId3, 300);
112113

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Copyright (C) 2022 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Text;
9+
using NUnit.Framework;
10+
using Xtensive.Orm.Configuration;
11+
using Xtensive.Orm.Tests.Storage.Multinode.QueryCachingTestModel;
12+
13+
namespace Xtensive.Orm.Tests.Storage.Multinode
14+
{
15+
public sealed class StaleQueryCacheForReAddedNodes : MultinodeTest
16+
{
17+
private const string DefaultSchema = WellKnownSchemas.Schema1;
18+
private const string Schema1 = WellKnownSchemas.Schema2;
19+
private const string Schema2 = WellKnownSchemas.Schema3;
20+
21+
private readonly object SimpleQueryKey = new object();
22+
23+
protected override void CheckRequirements() =>
24+
Require.AllFeaturesSupported(Orm.Providers.ProviderFeatures.Multischema);
25+
26+
protected override DomainConfiguration BuildConfiguration()
27+
{
28+
CustomUpgradeHandler.TypeIdPerNode.Clear();
29+
CustomUpgradeHandler.TypeIdPerNode.Add(TestNodeId2, 100);
30+
CustomUpgradeHandler.TypeIdPerNode.Add(TestNodeId3, 100);
31+
32+
var configuration = base.BuildConfiguration();
33+
configuration.Types.Register(typeof(BaseTestEntity).Assembly, typeof(BaseTestEntity).Namespace);
34+
configuration.UpgradeMode = DomainUpgradeMode.Recreate;
35+
configuration.DefaultSchema = DefaultSchema;
36+
return configuration;
37+
}
38+
39+
protected override void PopulateNodes()
40+
{
41+
CustomUpgradeHandler.CurrentNodeId = TestNodeId2;
42+
var nodeConfiguration = new NodeConfiguration(TestNodeId2);
43+
nodeConfiguration.SchemaMapping.Add(DefaultSchema, Schema1);
44+
nodeConfiguration.UpgradeMode = DomainUpgradeMode.Recreate;
45+
_ = Domain.StorageNodeManager.AddNode(nodeConfiguration);
46+
47+
CustomUpgradeHandler.CurrentNodeId = TestNodeId3;
48+
nodeConfiguration = new NodeConfiguration(TestNodeId3);
49+
nodeConfiguration.SchemaMapping.Add(DefaultSchema, Schema2);
50+
nodeConfiguration.UpgradeMode = DomainUpgradeMode.Recreate;
51+
_ = Domain.StorageNodeManager.AddNode(nodeConfiguration);
52+
}
53+
54+
protected override void PopulateData()
55+
{
56+
var nodes = new[] { WellKnown.DefaultNodeId, TestNodeId2, TestNodeId3 };
57+
58+
foreach (var nodeId in nodes) {
59+
var selectedNode = Domain.StorageNodeManager.GetNode(nodeId);
60+
using (var session = selectedNode.OpenSession())
61+
using (var tx = session.OpenTransaction()) {
62+
63+
var nodeIdName = string.IsNullOrEmpty(nodeId) ? "<default>" : nodeId;
64+
65+
_ = new BaseTestEntity(session) { BaseName = "A", BaseOwnerNodeId = nodeIdName };
66+
_ = new MiddleTestEntity(session) {
67+
BaseName = "AA",
68+
MiddleName = "AAM",
69+
BaseOwnerNodeId = nodeIdName,
70+
MiddleOwnerNodeId = nodeIdName
71+
};
72+
_ = new LeafTestEntity(session) {
73+
BaseName = "AAA",
74+
MiddleName = "AAAM",
75+
LeafName = "AAAL",
76+
BaseOwnerNodeId = nodeIdName,
77+
MiddleOwnerNodeId = nodeIdName,
78+
LeafOwnerNodeId = nodeIdName
79+
};
80+
81+
_ = new BaseTestEntity(session) { BaseName = "B", BaseOwnerNodeId = nodeIdName };
82+
_ = new MiddleTestEntity(session) {
83+
BaseName = "BB",
84+
MiddleName = "BBM",
85+
BaseOwnerNodeId = nodeIdName,
86+
MiddleOwnerNodeId = nodeIdName
87+
};
88+
_ = new LeafTestEntity(session) {
89+
BaseName = "BBB",
90+
MiddleName = "BBBM",
91+
LeafName = "BBBL",
92+
BaseOwnerNodeId = nodeIdName,
93+
MiddleOwnerNodeId = nodeIdName,
94+
LeafOwnerNodeId = nodeIdName
95+
};
96+
97+
_ = new BaseTestEntity(session) { BaseName = "C", BaseOwnerNodeId = nodeIdName };
98+
_ = new MiddleTestEntity(session) {
99+
BaseName = "CC",
100+
MiddleName = "CCM",
101+
BaseOwnerNodeId = nodeIdName,
102+
MiddleOwnerNodeId = nodeIdName
103+
};
104+
_ = new LeafTestEntity(session) {
105+
BaseName = "CCC",
106+
MiddleName = "CCCM",
107+
LeafName = "CCCL",
108+
BaseOwnerNodeId = nodeIdName,
109+
MiddleOwnerNodeId = nodeIdName,
110+
LeafOwnerNodeId = nodeIdName
111+
};
112+
113+
_ = new BaseTestEntity(session) { BaseName = "D", BaseOwnerNodeId = nodeIdName };
114+
_ = new MiddleTestEntity(session) {
115+
BaseName = "DD",
116+
MiddleName = "DDM",
117+
BaseOwnerNodeId = nodeIdName,
118+
MiddleOwnerNodeId = nodeIdName
119+
};
120+
_ = new LeafTestEntity(session) {
121+
BaseName = "DDD",
122+
MiddleName = "DDDM",
123+
LeafName = "DDDL",
124+
BaseOwnerNodeId = nodeIdName,
125+
MiddleOwnerNodeId = nodeIdName,
126+
LeafOwnerNodeId = nodeIdName
127+
};
128+
129+
// puts one query per each node to the query cache
130+
_ = ExecuteSimpleQueryCaching(session);
131+
132+
tx.Complete();
133+
}
134+
}
135+
}
136+
137+
[Test]
138+
public void ReAddNodeTest()
139+
{
140+
var node = Domain.StorageNodeManager.GetNode(TestNodeId2);
141+
var queryCacheSize = Domain.QueryCache.Count;
142+
143+
using (var session = node.OpenSession())
144+
using (var tx = session.OpenTransaction()) {
145+
_ = session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
146+
}
147+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
148+
149+
_ = Domain.StorageNodeManager.RemoveNode(TestNodeId2);
150+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
151+
152+
CustomUpgradeHandler.CurrentNodeId = TestNodeId2;
153+
var nodeConfiguration = new NodeConfiguration(TestNodeId2);
154+
nodeConfiguration.SchemaMapping.Add(DefaultSchema, Schema1);
155+
nodeConfiguration.UpgradeMode = DomainUpgradeMode.Validate;
156+
_ = Domain.StorageNodeManager.AddNode(nodeConfiguration);
157+
158+
node = Domain.StorageNodeManager.GetNode(TestNodeId2);
159+
160+
using (var session = node.OpenSession())
161+
using (var tx = session.OpenTransaction()) {
162+
_ = session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
163+
}
164+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
165+
166+
CustomUpgradeHandler.CurrentNodeId = TestNodeId3;
167+
nodeConfiguration = new NodeConfiguration(TestNodeId3);
168+
nodeConfiguration.SchemaMapping.Add(DefaultSchema, Schema2);
169+
nodeConfiguration.UpgradeMode = DomainUpgradeMode.Recreate;
170+
_ = Domain.StorageNodeManager.AddNode(nodeConfiguration);
171+
}
172+
173+
[Test]
174+
public void ReAddNodeWithAnotherSchemaMappingNoCacheCleanTest()
175+
{
176+
var node = Domain.StorageNodeManager.GetNode(TestNodeId2);
177+
var queryCacheSize = Domain.QueryCache.Count;
178+
179+
using (var session = node.OpenSession())
180+
using (var tx = session.OpenTransaction()) {
181+
_ = session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
182+
}
183+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
184+
185+
_ = Domain.StorageNodeManager.RemoveNode(TestNodeId2);
186+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
187+
188+
CustomUpgradeHandler.CurrentNodeId = TestNodeId2;
189+
var nodeConfiguration = new NodeConfiguration(TestNodeId2);
190+
nodeConfiguration.SchemaMapping.Add(DefaultSchema, Schema2);// uses schema of TestNodeId3
191+
nodeConfiguration.UpgradeMode = DomainUpgradeMode.Validate;
192+
_ = Domain.StorageNodeManager.AddNode(nodeConfiguration);
193+
194+
node = Domain.StorageNodeManager.GetNode(TestNodeId2);
195+
196+
using (var session = node.OpenSession())
197+
using (var tx = session.OpenTransaction()) {
198+
var results = session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
199+
foreach(var item in results) {
200+
Assert.That(item.BaseOwnerNodeId, Is.EqualTo(node.Id)); // gets result from old schema
201+
}
202+
}
203+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
204+
}
205+
206+
[Test]
207+
public void ReAddNodeWithAnotherSchemaMappingWithCacheCleanTest()
208+
{
209+
var node = Domain.StorageNodeManager.GetNode(TestNodeId2);
210+
var queryCacheSize = Domain.QueryCache.Count;
211+
212+
using (var session = node.OpenSession())
213+
using (var tx = session.OpenTransaction()) {
214+
_ = session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
215+
}
216+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
217+
218+
_ = Domain.StorageNodeManager.RemoveNode(TestNodeId2, true);
219+
Assert.That(Domain.QueryCache.Count, Is.LessThan(queryCacheSize));
220+
221+
CustomUpgradeHandler.CurrentNodeId = TestNodeId2;
222+
var nodeConfiguration = new NodeConfiguration(TestNodeId2);
223+
nodeConfiguration.SchemaMapping.Add(DefaultSchema, Schema2);// uses schema of TestNodeId3
224+
nodeConfiguration.UpgradeMode = DomainUpgradeMode.Validate;
225+
_ = Domain.StorageNodeManager.AddNode(nodeConfiguration);
226+
227+
node = Domain.StorageNodeManager.GetNode(TestNodeId2);
228+
229+
using (var session = node.OpenSession())
230+
using (var tx = session.OpenTransaction()) {
231+
var results = session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
232+
foreach (var item in results) {
233+
Assert.That(item.BaseOwnerNodeId, Is.Not.EqualTo(node.Id)); // gets result from correct schema but data was added by TestNodeId3
234+
}
235+
}
236+
Assert.That(Domain.QueryCache.Count, Is.EqualTo(queryCacheSize));
237+
}
238+
239+
private List<BaseTestEntity> ExecuteSimpleQueryCaching(Session session) =>
240+
session.Query.Execute(SimpleQueryKey, q => q.All<BaseTestEntity>().Where(e => e.BaseName.Contains("B"))).ToList();
241+
}
242+
}

Orm/Xtensive.Orm/Caching/LruCache{TKey, TItem}.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ protected virtual void Cleared() { }
189189

190190
#endregion
191191

192+
internal IEnumerable<TKey> GetKeysInternal()
193+
{
194+
foreach (KeyValuePair<TKey, TItem> cachedItem in deque)
195+
yield return cachedItem.Key;
196+
}
197+
192198

193199
// Constructors
194200

Orm/Xtensive.Orm/Orm/StorageNodeManager.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
// Created by: Denis Krjuchkov
55
// Created: 2014.03.13
66

7+
using System;
8+
using System.Linq;
79
using System.Threading;
810
using System.Threading.Tasks;
911
using JetBrains.Annotations;
12+
using Xtensive.Core;
1013
using Xtensive.Orm.Configuration;
1114
using Xtensive.Orm.Providers;
1215
using Xtensive.Orm.Upgrade;
@@ -48,12 +51,24 @@ public async Task<bool> AddNodeAsync([NotNull] NodeConfiguration configuration,
4851
/// Removes node with specified <paramref name="nodeId"/>.
4952
/// </summary>
5053
/// <param name="nodeId">Node identifier.</param>
54+
/// <param name="clearQueryCache">
55+
/// if <see langword="true"/> then cached queries dedicated to the removing node will be removed from cache as well. By default <see langword="false"/>.
56+
/// </param>
5157
/// <returns>True if node was removed, otherwise false.</returns>
52-
public bool RemoveNode([NotNull] string nodeId)
58+
public bool RemoveNode([NotNull] string nodeId, bool clearQueryCache = false)
5359
{
54-
return handlers.StorageNodeRegistry.Remove(nodeId);
60+
var removeResult = handlers.StorageNodeRegistry.Remove(nodeId);
61+
62+
if (removeResult && clearQueryCache) {
63+
var queryCache = (Caching.LruCache<object, Pair<object, Linq.TranslatedQuery>>) handlers.Domain.QueryCache;
64+
foreach (var key in queryCache.GetKeysInternal().Where(k => k is Pair<object, string> p && p.Second == nodeId).ToChainedBuffer()) {
65+
queryCache.RemoveKey(key, true);
66+
}
67+
}
68+
return removeResult;
5569
}
5670

71+
5772
/// <summary>
5873
/// Gets node with the specified <paramref name="nodeId"/>
5974
/// </summary>

0 commit comments

Comments
 (0)