From 5d0f1035513dabff9af57e74bb516027bb1c9072 Mon Sep 17 00:00:00 2001
From: Heinrichs <Heinrichs@itc.rwth-aachen.de>
Date: Thu, 3 Nov 2022 15:00:45 +0100
Subject: [PATCH] Fix: Retry failed Virtuoso Queries

---
 .../Implementations/ResourceStructuralData.cs |  4 +-
 src/SQL2Linked/SQL2Linked.csproj              |  3 +-
 src/SQL2Linked/StructuralData.cs              | 45 ++++++++++++++++---
 3 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/src/SQL2Linked/Implementations/ResourceStructuralData.cs b/src/SQL2Linked/Implementations/ResourceStructuralData.cs
index 2202e87..cccc6af 100644
--- a/src/SQL2Linked/Implementations/ResourceStructuralData.cs
+++ b/src/SQL2Linked/Implementations/ResourceStructuralData.cs
@@ -147,7 +147,7 @@ namespace SQL2Linked.Implementations
                                      "}"
                 };
 
-                var resultSet = QueryEndpoint.QueryWithResultSet(cmdString.ToString());
+                var resultSet = WrapRequest(() => RdfStoreConnector.QueryEndpoint.QueryWithResultSet(cmdString.ToString()));
 
                 foreach (var result in resultSet)
                 {
@@ -211,7 +211,7 @@ namespace SQL2Linked.Implementations
                                 "WHERE { @applicationProfile <http://www.w3.org/ns/shacl#targetClass> ?targetClass }"
             };
             targetClassCmdString.SetUri("applicationProfile", new Uri(entry.ApplicationProfile));
-            var targetClassResultSet = QueryEndpoint.QueryWithResultSet(targetClassCmdString.ToString());
+            var targetClassResultSet = WrapRequest(() => RdfStoreConnector.QueryEndpoint.QueryWithResultSet(targetClassCmdString.ToString()));
 
             var targetClass = entry.ApplicationProfile;
             foreach (var result in targetClassResultSet)
diff --git a/src/SQL2Linked/SQL2Linked.csproj b/src/SQL2Linked/SQL2Linked.csproj
index eea70a1..6611386 100644
--- a/src/SQL2Linked/SQL2Linked.csproj
+++ b/src/SQL2Linked/SQL2Linked.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
@@ -10,6 +10,7 @@
   <ItemGroup>
     <PackageReference Include="Coscine.Database" Version="2.*-*" />
     <PackageReference Include="Coscine.Metadata" Version="2.*-*" />
+    <PackageReference Include="Polly" Version="7.2.3" />
   </ItemGroup>
 
 </Project>
diff --git a/src/SQL2Linked/StructuralData.cs b/src/SQL2Linked/StructuralData.cs
index 0b86a3a..53c7a0c 100644
--- a/src/SQL2Linked/StructuralData.cs
+++ b/src/SQL2Linked/StructuralData.cs
@@ -1,8 +1,8 @@
-using Coscine.Configuration;
+using Polly;
+using Coscine.Configuration;
 using Coscine.Database.Models;
 using Coscine.Metadata;
 using VDS.RDF;
-using VDS.RDF.Query;
 
 namespace SQL2Linked
 {
@@ -12,7 +12,6 @@ namespace SQL2Linked
         public ConsulConfiguration Configuration { get; init; }
         public RdfStoreConnector RdfStoreConnector { get; init; }
         public static string Prefix { get; set; }
-        public readonly SparqlRemoteEndpoint QueryEndpoint;
 
         public StructuralData()
         {
@@ -20,7 +19,11 @@ namespace SQL2Linked
             RdfStoreConnector = new RdfStoreConnector(Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url"));
             Model = new T();
             Prefix = Configuration.GetStringAndWait("coscine/global/epic/prefix");
-            QueryEndpoint = new SparqlRemoteEndpoint(new Uri(Configuration.GetStringAndWait("coscine/local/virtuoso/additional/url")));
+            // 100 second timeout
+            var timeout = 100000;
+            RdfStoreConnector.QueryEndpoint.Timeout = timeout;
+            RdfStoreConnector.UpdateEndpoint.Timeout = timeout;
+            RdfStoreConnector.ReadWriteSparqlConnector.Timeout = timeout;
         }
 
         public abstract IEnumerable<IGraph> ConvertToLinkedData(IEnumerable<S> entries);
@@ -46,17 +49,17 @@ namespace SQL2Linked
             foreach (var graph in graphs)
             {
                 Console.WriteLine($" ({graph.BaseUri})");
-                var exists = RdfStoreConnector.HasGraph(graph.BaseUri);
+                var exists = WrapRequest(() => RdfStoreConnector.HasGraph(graph.BaseUri));
                 if (exists)
                 {
                     Console.WriteLine($" - Graph {graph.BaseUri} exists");
 
                     // Clear the existing graph from the store
-                    RdfStoreConnector.ClearGraph(graph.BaseUri);
+                    WrapRequest(() => RdfStoreConnector.ClearGraph(graph.BaseUri));
                     Console.WriteLine($" - Cleared Graph {graph.BaseUri}");
                 }
                 // Add the new graph to the store
-                RdfStoreConnector.AddGraph(graph);
+                WrapRequest(() => RdfStoreConnector.AddGraph(graph));
                 Console.WriteLine($" - Graph {graph.BaseUri} added successfully");
                 Console.WriteLine();
             }
@@ -103,5 +106,33 @@ namespace SQL2Linked
                 );
             }
         }
+
+        /// <summary>
+        /// Retry Virtuoso Requests since they sometimes just fail
+        /// </summary>
+        /// <typeparam name="W"></typeparam>
+        /// <param name="function"></param>
+        /// <returns></returns>
+        public void WrapRequest(Action action)
+        {
+            Policy
+                .Handle<Exception>()
+                .WaitAndRetry(5, retryNumber => TimeSpan.FromMilliseconds(200))
+                .Execute(() => action.Invoke());
+        }
+
+        /// <summary>
+        /// Retry Virtuoso Requests since they sometimes just fail
+        /// </summary>
+        /// <typeparam name="W"></typeparam>
+        /// <param name="function"></param>
+        /// <returns></returns>
+        public W WrapRequest<W>(Func<W> function)
+        {
+            return Policy
+                .Handle<Exception>()
+                .WaitAndRetry(5, retryNumber => TimeSpan.FromMilliseconds(200))
+                .ExecuteAndCapture(() => function.Invoke()).Result;
+        }
     }
 }
-- 
GitLab