From 32e386fd444a4a9148b41712c992bb4edb4ab8d5 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 16 Apr 2024 10:33:05 +0200 Subject: [PATCH 1/4] Add methodology for directly calling upgrades from main process rather than via pipes --- Versioning_Engine/Convert/ToNewVersion.cs | 201 ++++++++++++++++++---- 1 file changed, 163 insertions(+), 38 deletions(-) diff --git a/Versioning_Engine/Convert/ToNewVersion.cs b/Versioning_Engine/Convert/ToNewVersion.cs index 58431f31b..773ffe877 100644 --- a/Versioning_Engine/Convert/ToNewVersion.cs +++ b/Versioning_Engine/Convert/ToNewVersion.cs @@ -83,58 +83,106 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi // Get the list of upgraders to call List versions = Query.UpgradersToCall(version); + bool versionWithPipes = false; + lock (m_versioningLock) { - // Call all the upgraders in sequence - for (int i = 0; i < versions.Count; i++) + if (versionWithPipes) { - // Create a connection with the upgrader - NamedPipeServerStream pipe = GetPipe(versions[i]); - if (pipe == null) - return document; + // Call all the upgraders in sequence + for (int i = 0; i < versions.Count; i++) + { + // Create a connection with the upgrader + NamedPipeServerStream pipe = GetPipe(versions[i]); + if (pipe == null) + return document; - // Send the document - SendDocument(document, pipe); + // Send the document + SendDocument(document, pipe); - // Get the new version back - BsonDocument result = ReadDocument(pipe); - if (result != null) - { - if (result.Contains("_t") && result["_t"] == "NoUpdate") + // Get the new version back + BsonDocument result = ReadDocument(pipe); + if (result != null) { - if (result.Contains("Message")) + if (result.Contains("_t") && result["_t"] == "NoUpdate") + { + if (result.Contains("Message")) + { + noUpdateMessage = result["Message"].ToString(); + Engine.Base.Compute.RecordError(noUpdateMessage); + } + } + else if (document != result) { - noUpdateMessage = result["Message"].ToString(); - Engine.Base.Compute.RecordError(noUpdateMessage); + wasUpdated = true; + document = result; } } - else if (document != result) + } + + // Record the fact that a document needed to be upgraded + if (wasUpdated || noUpdateMessage != null) + { + string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); + string newVersion = Engine.Base.Query.BHoMVersion(); + string oldVersion = string.IsNullOrWhiteSpace(version) ? "?.?" : version; + string message = noUpdateMessage ?? $"{oldDocument} from version {oldVersion} has been upgraded to {newDocument} (version {newVersion})"; + + BH.Engine.Base.Compute.RecordEvent(new VersioningEvent + { + OldDocument = oldDocument, + NewDocument = newDocument, + OldVersion = oldVersion, + NewVersion = newVersion, + Message = message + }); + } + } + else + { + // Call all the upgraders in sequence + for (int i = 0; i < versions.Count; i++) + { + BsonDocument result = null; + try + { + //Get pre-compiled upgrader method for the version + Func upgrader = GetUpgraderMethod(versions[i]); + result = upgrader(document); //Upgrade + } + catch (Exception e) + { + Engine.Base.Compute.RecordError(e.Message); + if (e.GetType().Name == "NoUpdateException") //No update -> no point in trying the rest + break; + } + + if (result != null && document != result) { wasUpdated = true; document = result; } } - } - } - // Record the fact that a document needed to be upgraded - if (wasUpdated || noUpdateMessage != null) - { - string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); - string newVersion = Engine.Base.Query.BHoMVersion(); - string oldVersion = string.IsNullOrWhiteSpace(version) ? "?.?" : version; - string message = noUpdateMessage ?? $"{oldDocument} from version {oldVersion} has been upgraded to {newDocument} (version {newVersion})"; + if (wasUpdated) + { + string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); + string newVersion = Engine.Base.Query.BHoMVersion(); + string oldVersion = string.IsNullOrWhiteSpace(version) ? "?.?" : version; + string message = noUpdateMessage ?? $"{oldDocument} from version {oldVersion} has been upgraded to {newDocument} (version {newVersion})"; - BH.Engine.Base.Compute.RecordEvent(new VersioningEvent - { - OldDocument = oldDocument, - NewDocument = newDocument, - OldVersion = oldVersion, - NewVersion = newVersion, - Message = message - }); + BH.Engine.Base.Compute.RecordEvent(new VersioningEvent + { + OldDocument = oldDocument, + NewDocument = newDocument, + OldVersion = oldVersion, + NewVersion = newVersion, + Message = message + }); + } + } } - + return document; } @@ -167,7 +215,7 @@ private static NamedPipeServerStream GetPipe(string version) // Find the upgrader file string upgraderName = "BHoMUpgrader" + version.Replace(".", ""); - string processFile = upgraderName + "\\" + upgraderName + ".exe"; + string processFile = upgraderName + "\\" + upgraderName + ".exe"; if (!File.Exists(processFile)) { processFile = Path.Combine(BH.Engine.Base.Query.BHoMFolderUpgrades(), processFile); @@ -184,11 +232,11 @@ private static NamedPipeServerStream GetPipe(string version) process.StartInfo = new ProcessStartInfo(processFile, pipeName); process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; - bool ok = process.Start(); + bool ok = process.Start(); // Waiting for pipe to connect pipe.WaitForConnection(); - + // Store the pipe m_Pipes[version] = pipe; @@ -229,6 +277,73 @@ private static BsonDocument ReadDocument(PipeStream pipe) return BsonSerializer.Deserialize(content, typeof(BsonDocument)) as BsonDocument; } + /***************************************************/ + + private static Func GetUpgraderMethod(string version) + { + // Return the pipe if already exists + if (m_CompiledUpgraders.ContainsKey(version)) + return m_CompiledUpgraders[version]; + + + // Find the upgrader file + string upgraderName = "BHoMUpgrader" + version.Replace(".", ""); + string processFile = upgraderName + "\\" + upgraderName + ".exe"; + if (!File.Exists(processFile)) + { + processFile = Path.Combine(BH.Engine.Base.Query.BHoMFolderUpgrades(), processFile); + + if (!File.Exists(processFile)) + { + Base.Compute.RecordWarning(processFile.Split(new char[] { '\\' }).Last() + " is missing. The object will not be upgraded"); + return null; + } + } + + Assembly converterAssembly = Assembly.LoadFrom(processFile); + + if (m_BaseConverter == null) + { + Assembly upgraderAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.GetName().Name == "BHoMUpgrader"); + if (upgraderAssembly == null) + { + string baseUpgraderName = "BHoMUpgrader" + version.Replace(".", ""); + string baseProcessFile = upgraderName + "\\" + "BHoMUpgrader" + ".dll"; + if (!File.Exists(baseProcessFile)) + { + baseProcessFile = Path.Combine(BH.Engine.Base.Query.BHoMFolderUpgrades(), baseProcessFile); + + if (!File.Exists(baseProcessFile)) + { + Base.Compute.RecordWarning(baseProcessFile.Split(new char[] { '\\' }).Last() + " is missing. The object will not be upgraded"); + return null; + } + } + upgraderAssembly = Assembly.LoadFrom(baseProcessFile); + } + Type upgraderType = upgraderAssembly.GetTypes().First(x => x.Name == "Upgrader"); + m_Upgrader = Activator.CreateInstance(upgraderType); + m_UpgraderMethod = upgraderType.GetMethod("Upgrade", BindingFlags.NonPublic | BindingFlags.Instance); + + Type baseConverterType = upgraderAssembly.GetTypes().First(x => x.Name == "Converter"); + m_BaseConverter = Activator.CreateInstance(baseConverterType); + } + + Type converterType = converterAssembly.GetTypes().First(x => m_BaseConverter.GetType().IsAssignableFrom(x)); + dynamic converter = Activator.CreateInstance(converterType); + + Func func = CreateUpgradeFunction(converter as dynamic, m_BaseConverter as dynamic); + m_CompiledUpgraders[version] = func; + return func; + } + + /***************************************************/ + + private static Func CreateUpgradeFunction(T converter, TBase baseConverter) where T : class where TBase : class + { + Func initFunc = (Func)Delegate.CreateDelegate(typeof(Func), m_Upgrader, m_UpgraderMethod); + return x => initFunc(x, converter as TBase); + } /***************************************************/ /**** Private Fields ****/ @@ -239,6 +354,16 @@ private static BsonDocument ReadDocument(PipeStream pipe) private static object m_versioningLock = new object(); /***************************************************/ + + private static Dictionary> m_CompiledUpgraders = new Dictionary>(); + + private static object m_Upgrader = null; + + private static dynamic m_BaseConverter = null; + + private static MethodInfo m_UpgraderMethod = null; + + /***************************************************/ } } From a9a4f98eda8c9798a0e4a408d775303df6a1c705 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Wed, 17 Apr 2024 15:53:56 +0200 Subject: [PATCH 2/4] Tweak to make the new system to give identical results to previous version Potential scope to improve the behaviour compared to previous version, but for this first system change prefer to make the systems behave literally identical to ensure this switch gives as small impact in terms of functionality as possible. --- Versioning_Engine/Convert/ToNewVersion.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Versioning_Engine/Convert/ToNewVersion.cs b/Versioning_Engine/Convert/ToNewVersion.cs index 773ffe877..4b19176fe 100644 --- a/Versioning_Engine/Convert/ToNewVersion.cs +++ b/Versioning_Engine/Convert/ToNewVersion.cs @@ -153,8 +153,11 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi catch (Exception e) { Engine.Base.Compute.RecordError(e.Message); - if (e.GetType().Name == "NoUpdateException") //No update -> no point in trying the rest - break; + if (e.GetType().Name == "NoUpdateException") + { + noUpdateMessage = e.Message; + result = null; + } } if (result != null && document != result) @@ -164,7 +167,7 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi } } - if (wasUpdated) + if (wasUpdated || noUpdateMessage != null) { string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); string newVersion = Engine.Base.Query.BHoMVersion(); From a1f6a0e95d9ca30a664ad9d0fe6122322d75c1d4 Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Thu, 18 Apr 2024 09:52:26 +0200 Subject: [PATCH 3/4] Minor tweak to Error handling for general exceptions thrown by the upgrader --- Versioning_Engine/Convert/ToNewVersion.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Versioning_Engine/Convert/ToNewVersion.cs b/Versioning_Engine/Convert/ToNewVersion.cs index 4b19176fe..e0cec3b52 100644 --- a/Versioning_Engine/Convert/ToNewVersion.cs +++ b/Versioning_Engine/Convert/ToNewVersion.cs @@ -152,12 +152,17 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi } catch (Exception e) { - Engine.Base.Compute.RecordError(e.Message); + if (e.GetType().Name == "NoUpdateException") { + Engine.Base.Compute.RecordError(e.Message); noUpdateMessage = e.Message; result = null; } + else + { + Engine.Base.Compute.RecordError(e, "BHoMUpgrader exception:"); + } } if (result != null && document != result) From 01695af3b189acc664c25b20ef273dd151cdf72f Mon Sep 17 00:00:00 2001 From: Isak Naslund Date: Tue, 23 Apr 2024 11:18:20 +0200 Subject: [PATCH 4/4] Slight code clean-up and refactoring avoiding code duplication --- Versioning_Engine/Convert/ToNewVersion.cs | 100 +++++++++------------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/Versioning_Engine/Convert/ToNewVersion.cs b/Versioning_Engine/Convert/ToNewVersion.cs index e0cec3b52..a25de1139 100644 --- a/Versioning_Engine/Convert/ToNewVersion.cs +++ b/Versioning_Engine/Convert/ToNewVersion.cs @@ -119,24 +119,6 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi } } } - - // Record the fact that a document needed to be upgraded - if (wasUpdated || noUpdateMessage != null) - { - string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); - string newVersion = Engine.Base.Query.BHoMVersion(); - string oldVersion = string.IsNullOrWhiteSpace(version) ? "?.?" : version; - string message = noUpdateMessage ?? $"{oldDocument} from version {oldVersion} has been upgraded to {newDocument} (version {newVersion})"; - - BH.Engine.Base.Compute.RecordEvent(new VersioningEvent - { - OldDocument = oldDocument, - NewDocument = newDocument, - OldVersion = oldVersion, - NewVersion = newVersion, - Message = message - }); - } } else { @@ -148,11 +130,11 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi { //Get pre-compiled upgrader method for the version Func upgrader = GetUpgraderMethod(versions[i]); - result = upgrader(document); //Upgrade + if (upgrader != null) + result = upgrader(document); //Upgrade } catch (Exception e) { - if (e.GetType().Name == "NoUpdateException") { Engine.Base.Compute.RecordError(e.Message); @@ -171,26 +153,25 @@ public static BsonDocument ToNewVersion(this BsonDocument document, string versi document = result; } } - - if (wasUpdated || noUpdateMessage != null) - { - string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); - string newVersion = Engine.Base.Query.BHoMVersion(); - string oldVersion = string.IsNullOrWhiteSpace(version) ? "?.?" : version; - string message = noUpdateMessage ?? $"{oldDocument} from version {oldVersion} has been upgraded to {newDocument} (version {newVersion})"; - - BH.Engine.Base.Compute.RecordEvent(new VersioningEvent - { - OldDocument = oldDocument, - NewDocument = newDocument, - OldVersion = oldVersion, - NewVersion = newVersion, - Message = message - }); - } } } + if (wasUpdated || noUpdateMessage != null) + { + string newDocument = noUpdateMessage != null ? null : Compute.VersioningKey(document); + string newVersion = Engine.Base.Query.BHoMVersion(); + string oldVersion = string.IsNullOrWhiteSpace(version) ? "?.?" : version; + string message = noUpdateMessage ?? $"{oldDocument} from version {oldVersion} has been upgraded to {newDocument} (version {newVersion})"; + + BH.Engine.Base.Compute.RecordEvent(new VersioningEvent + { + OldDocument = oldDocument, + NewDocument = newDocument, + OldVersion = oldVersion, + NewVersion = newVersion, + Message = message + }); + } return document; } @@ -287,6 +268,23 @@ private static BsonDocument ReadDocument(PipeStream pipe) /***************************************************/ + private static string FindFile(string path) + { + if (!File.Exists(path)) + { + path = Path.Combine(BH.Engine.Base.Query.BHoMFolderUpgrades(), path); + + if (!File.Exists(path)) + { + Base.Compute.RecordWarning(path.Split(new char[] { '\\' }).Last() + " is missing. The object will not be upgraded"); + return null; + } + } + return path; + } + + /***************************************************/ + private static Func GetUpgraderMethod(string version) { // Return the pipe if already exists @@ -296,17 +294,9 @@ private static Func GetUpgraderMethod(string version // Find the upgrader file string upgraderName = "BHoMUpgrader" + version.Replace(".", ""); - string processFile = upgraderName + "\\" + upgraderName + ".exe"; - if (!File.Exists(processFile)) - { - processFile = Path.Combine(BH.Engine.Base.Query.BHoMFolderUpgrades(), processFile); - - if (!File.Exists(processFile)) - { - Base.Compute.RecordWarning(processFile.Split(new char[] { '\\' }).Last() + " is missing. The object will not be upgraded"); - return null; - } - } + string processFile = FindFile(upgraderName + "\\" + upgraderName + ".exe"); + if (processFile == null) + return null; Assembly converterAssembly = Assembly.LoadFrom(processFile); @@ -316,17 +306,9 @@ private static Func GetUpgraderMethod(string version if (upgraderAssembly == null) { string baseUpgraderName = "BHoMUpgrader" + version.Replace(".", ""); - string baseProcessFile = upgraderName + "\\" + "BHoMUpgrader" + ".dll"; - if (!File.Exists(baseProcessFile)) - { - baseProcessFile = Path.Combine(BH.Engine.Base.Query.BHoMFolderUpgrades(), baseProcessFile); - - if (!File.Exists(baseProcessFile)) - { - Base.Compute.RecordWarning(baseProcessFile.Split(new char[] { '\\' }).Last() + " is missing. The object will not be upgraded"); - return null; - } - } + string baseProcessFile = FindFile(upgraderName + "\\" + "BHoMUpgrader" + ".dll"); + if (baseUpgraderName == null) + return null; upgraderAssembly = Assembly.LoadFrom(baseProcessFile); } Type upgraderType = upgraderAssembly.GetTypes().First(x => x.Name == "Upgrader");