Browse Source

Improved speed of deletions

Kenric Nugteren 1 year ago
parent
commit
43a2a6b4b8
1 changed files with 41 additions and 18 deletions
  1. 41 18
      inabox.database.sqlite/SQLiteProvider.cs

+ 41 - 18
inabox.database.sqlite/SQLiteProvider.cs

@@ -8,6 +8,7 @@ using System.Runtime.Serialization.Formatters.Binary;
 using System.Text;
 using InABox.Core;
 using Microsoft.CodeAnalysis;
+using NPOI.POIFS.FileSystem;
 
 namespace InABox.Database.SQLite
 {
@@ -2706,6 +2707,9 @@ namespace InABox.Database.SQLite
 
         private Dictionary<Type, List<Tuple<Type, string>>> _cascades = new();
         private Dictionary<Type, List<Tuple<Type, List<string>>>> _setNulls = new();
+
+        private const int deleteBatchSize = 100;
+
         private void LoadDeletions(Type type)
         {
             // Get the EntityLink that is associated with this class
@@ -2762,55 +2766,74 @@ namespace InABox.Database.SQLite
 
         private MethodInfo _deleteEntitiesMethod = typeof(SQLiteProvider).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
             .Single(x => x.Name == nameof(DeleteEntity) && x.IsGenericMethod);
-        private void DeleteEntity<T>(Guid parentID, string parentField, DeletionData deletionData) where T : Entity, new()
+        private void DeleteEntity<T>(Guid[] parentIDs, string parentField, DeletionData deletionData) where T : Entity, new()
         {
             var columns = DeletionData.DeletionColumns<T>();
-            var entities = Query(new Filter<T>(parentField).IsEqualTo(parentID), columns);
-            foreach(var row in entities.Rows)
+
+            var entityIDs = new List<Guid>();
+
+            for (int i = 0; i < parentIDs.Length; i += deleteBatchSize)
             {
-                deletionData.DeleteEntity<T>(row);
-                CascadeDelete(typeof(T), row.Get<T, Guid>(x => x.ID), deletionData);
+                var items = new ArraySegment<Guid>(parentIDs, i, Math.Min(deleteBatchSize, parentIDs.Length - i));
+                var entities = Query(new Filter<T>(parentField).InList(items.ToArray()), columns);
+                foreach (var row in entities.Rows)
+                {
+                    deletionData.DeleteEntity<T>(row);
+                    entityIDs.Add(row.Get<T, Guid>(x => x.ID));
+                }
             }
+            CascadeDelete(typeof(T), entityIDs.ToArray(), deletionData);
         }
        
-        private void DeleteEntity(Type T, Guid parentID, string parentField, DeletionData deletionData)
+        private void DeleteEntity(Type T, Guid[] parentIDs, string parentField, DeletionData deletionData)
         {
-            _deleteEntitiesMethod.MakeGenericMethod(T).Invoke(this, new object?[] { parentID, parentField, deletionData });
+            _deleteEntitiesMethod.MakeGenericMethod(T).Invoke(this, new object?[] { parentIDs, parentField, deletionData });
         }
 
         private MethodInfo _setNullEntityMethod = typeof(SQLiteProvider).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
             .Single(x => x.Name == nameof(SetNullEntity) && x.IsGenericMethod);
-        private void SetNullEntity<T>(List<string> properties, Guid parentID, DeletionData deletionData) where T : Entity, new()
+        private void SetNullEntity<T>(List<string> properties, Guid[] parentIDs, DeletionData deletionData) where T : Entity, new()
         {
             foreach(var property in properties)
             {
-                var entities = Query(new Filter<T>(property).IsEqualTo(parentID), new Columns<T>(x => x.ID));
-                foreach (var row in entities.Rows)
+                var columns = new Columns<T>(x => x.ID);
+                columns.Add(property);
+
+                for(int i = 0; i < parentIDs.Length; i += deleteBatchSize)
                 {
-                    deletionData.SetNullEntity<T>(row.Get<T, Guid>(x => x.ID), property, parentID);
+                    var items = new ArraySegment<Guid>(parentIDs, i, Math.Min(deleteBatchSize, parentIDs.Length - i));
+                    var entities = Query(new Filter<T>(property).InList(items.ToArray()), columns);
+                    foreach (var row in entities.Rows)
+                    {
+                        deletionData.SetNullEntity<T>(row.Get<T, Guid>(x => x.ID), property, row.Get<Guid>(property));
+                    }
                 }
             }
         }
 
-        private void SetNullEntity(Type T, List<string> properties, Guid parentID, DeletionData deletionData)
+        private void SetNullEntity(Type T, List<string> properties, Guid[] parentIDs, DeletionData deletionData)
         {
-            _setNullEntityMethod.MakeGenericMethod(T).Invoke(this, new object?[] { properties, parentID, deletionData });
+            _setNullEntityMethod.MakeGenericMethod(T).Invoke(this, new object?[] { properties, parentIDs, deletionData });
         }
 
-        private void CascadeDelete(Type type, Guid parentID, DeletionData deletionData)
+        private void CascadeDelete(Type type, Guid[] parentIDs, DeletionData deletionData)
         {
+            if(parentIDs.Length == 0)
+            {
+                return;
+            }
             if (_cascades.TryGetValue(type, out var cascades))
             {
                 foreach (var cascade in cascades)
                 {
-                    DeleteEntity(cascade.Item1, parentID, cascade.Item2, deletionData);
+                    DeleteEntity(cascade.Item1, parentIDs, cascade.Item2, deletionData);
                 }
             }
             if(_setNulls.TryGetValue(type, out var setNulls))
             {
                 foreach(var setNull in setNulls)
                 {
-                    SetNullEntity(setNull.Item1, setNull.Item2, parentID, deletionData);
+                    SetNullEntity(setNull.Item1, setNull.Item2, parentIDs, deletionData);
                 }
             }
         }
@@ -2832,7 +2855,7 @@ namespace InABox.Database.SQLite
 
             var deletionData = new DeletionData();
             deletionData.DeleteEntity(entity);
-            CascadeDelete(typeof(T), entity.ID, deletionData);
+            CascadeDelete(typeof(T), new Guid[] { entity.ID }, deletionData);
 
             var tableName = typeof(T).Name;
             var deletion = new Deletion()
@@ -2866,8 +2889,8 @@ namespace InABox.Database.SQLite
             foreach (var entity in entityList)
             {
                 deletionData.DeleteEntity(entity);
-                CascadeDelete(typeof(T), entity.ID, deletionData);
             }
+            CascadeDelete(typeof(T), ids, deletionData);
 
             var tableName = typeof(T).Name;
             var deletion = new Deletion()