[optimize] Database optimizition

This commit is contained in:
acite
2025-09-20 15:03:05 +08:00
parent 87536a1508
commit f1e636a79d
3 changed files with 46 additions and 59 deletions

View File

@@ -11,7 +11,8 @@
<component name="ChangeListManager">
<list default="true" id="bf317275-3039-49bb-a475-725a800a0cce" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.Abyss/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/RootController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Controllers/Security/RootController.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss.sln" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss.sln" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Abyss/Components/Services/ResourceService.cs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -228,6 +229,10 @@
<workItem from="1758307433345" duration="34000" />
<workItem from="1758344749532" duration="238000" />
<workItem from="1758345893755" duration="2662000" />
<workItem from="1758349313244" duration="24000" />
<workItem from="1758349710909" duration="16000" />
<workItem from="1758350096355" duration="452000" />
<workItem from="1758350848039" duration="820000" />
</task>
<servers />
</component>

View File

@@ -2,8 +2,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abyss", "Abyss\Abyss.csproj", "{3337C1CD-2419-4922-BC92-AF1A825DDF23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbyssCli", "AbyssCli\AbyssCli.csproj", "{D7D668D4-61E7-4AA4-B615-A162FABAD333}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -14,9 +12,5 @@ Global
{3337C1CD-2419-4922-BC92-AF1A825DDF23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3337C1CD-2419-4922-BC92-AF1A825DDF23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3337C1CD-2419-4922-BC92-AF1A825DDF23}.Release|Any CPU.Build.0 = Release|Any CPU
{D7D668D4-61E7-4AA4-B615-A162FABAD333}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7D668D4-61E7-4AA4-B615-A162FABAD333}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7D668D4-61E7-4AA4-B615-A162FABAD333}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7D668D4-61E7-4AA4-B615-A162FABAD333}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -531,7 +531,8 @@ public class ResourceService
}
else
{
_logger.LogDebug($"Query: access denied or not managed for '{entry}' (user token: {token}) - item skipped.");
_logger.LogDebug(
$"Query: access denied or not managed for '{entry}' (user token: {token}) - item skipped.");
}
}
catch (Exception exEntry)
@@ -788,33 +789,32 @@ public class ResourceService
return false;
}
// Normalize path to full path (Valid / ValidAll expect absolute path starting with media root)
// Normalize path to full path
path = Path.GetFullPath(path);
// If recursive and path is directory, collect all descendants (iterative)
// Collect targets and permission checks
List<string> targets = new List<string>();
try
{
if (recursive && Directory.Exists(path))
{
// include root directory itself
_logger.LogInformation($"Recursive directory '{path}'.");
targets.Add(path);
// Enumerate all files and directories under path iteratively
foreach (var entry in Directory.EnumerateFileSystemEntries(path, "*", SearchOption.AllDirectories))
{
targets.Add(entry);
}
// Permission check for all targets
if (!await ValidAll(targets.ToArray(), token, OperationType.Security, ip))
{
_logger.LogWarning($"Permission denied for recursive chmod on '{path}'");
return false;
}
_logger.LogInformation($"Successfully validated chmod on '{path}'.");
}
else
{
// Non-recursive or target is a file: validate single path
if (!await Valid(path, token, OperationType.Security, ip))
{
_logger.LogWarning($"Permission denied for chmod on '{path}'");
@@ -824,8 +824,9 @@ public class ResourceService
targets.Add(path);
}
// Convert targets to relative paths and UIDs
var relUids = targets.Select(t => Path.GetRelativePath(_config.MediaRoot, t))
// Build distinct UIDs
var relUids = targets
.Select(t => Path.GetRelativePath(_config.MediaRoot, t))
.Select(rel => Uid(rel))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
@@ -836,35 +837,28 @@ public class ResourceService
return false;
}
// Batch query existing ResourceAttribute rows for these UIDs (chunk to respect SQLite param limit)
var rows = new List<ResourceAttribute>();
const int sqliteMaxVariableNumber = 900;
// Chunked bulk UPDATE using SQL "UPDATE ... WHERE Uid IN (...)"
int updatedCount = 0;
const int sqliteMaxVariableNumber = 900; // leave some headroom for other params
for (int i = 0; i < relUids.Count; i += sqliteMaxVariableNumber)
{
var chunk = relUids.Skip(i).Take(sqliteMaxVariableNumber).ToList();
var placeholders = string.Join(",", chunk.Select(_ => "?"));
var queryArgs = chunk.Cast<object>().ToArray();
var sql = $"SELECT * FROM ResourceAttributes WHERE Uid IN ({placeholders})";
var chunkResult = await _database.QueryAsync<ResourceAttribute>(sql, queryArgs);
rows.AddRange(chunkResult);
}
// First param is permission, rest are Uid values
var args = new List<object> { permission };
args.AddRange(chunk);
var rowDict = rows.ToDictionary(r => r.Uid, StringComparer.OrdinalIgnoreCase);
int updatedCount = 0;
foreach (var uid in relUids)
{
if (rowDict.TryGetValue(uid, out var ra))
var sql = $"UPDATE ResourceAttributes SET Permission = ? WHERE Uid IN ({placeholders})";
try
{
ra.Permission = permission;
var res = await _database.UpdateAsync(ra);
if (res > 0) updatedCount++;
else _logger.LogError($"Failed to update permission row (UID={uid}) for chmod on '{path}'");
var rowsAffected = await _database.ExecuteAsync(sql, args.ToArray());
updatedCount += rowsAffected;
_logger.LogInformation($"Chmod chunk updated {rowsAffected} rows (chunk size {chunk.Count}).");
}
else
catch (Exception ex)
{
// Resource not managed by DB — skip but log
_logger.LogWarning($"Chmod skipped: resource not found in DB (Uid={uid}) for target '{path}'");
_logger.LogError(ex, $"Error executing chmod update chunk for path '{path}'.");
// continue with other chunks; do not abort whole operation on one chunk error
}
}
@@ -929,8 +923,9 @@ public class ResourceService
targets.Add(path);
}
// Build UID list
var relUids = targets.Select(t => Path.GetRelativePath(_config.MediaRoot, t))
// Build distinct UIDs
var relUids = targets
.Select(t => Path.GetRelativePath(_config.MediaRoot, t))
.Select(rel => Uid(rel))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
@@ -941,34 +936,27 @@ public class ResourceService
return false;
}
// Batch query DB
var rows = new List<ResourceAttribute>();
// Chunked bulk UPDATE: SET Owner = ? WHERE Uid IN (...)
int updatedCount = 0;
const int sqliteMaxVariableNumber = 900;
for (int i = 0; i < relUids.Count; i += sqliteMaxVariableNumber)
{
var chunk = relUids.Skip(i).Take(sqliteMaxVariableNumber).ToList();
var placeholders = string.Join(",", chunk.Select(_ => "?"));
var queryArgs = chunk.Cast<object>().ToArray();
var sql = $"SELECT * FROM ResourceAttributes WHERE Uid IN ({placeholders})";
var chunkResult = await _database.QueryAsync<ResourceAttribute>(sql, queryArgs);
rows.AddRange(chunkResult);
}
var args = new List<object> { owner };
args.AddRange(chunk);
var rowDict = rows.ToDictionary(r => r.Uid, StringComparer.OrdinalIgnoreCase);
int updatedCount = 0;
foreach (var uid in relUids)
{
if (rowDict.TryGetValue(uid, out var ra))
var sql = $"UPDATE ResourceAttributes SET Owner = ? WHERE Uid IN ({placeholders})";
try
{
ra.Owner = owner;
var res = await _database.UpdateAsync(ra);
if (res > 0) updatedCount++;
else _logger.LogError($"Failed to update owner row (UID={uid}) for chown on '{path}'");
var rowsAffected = await _database.ExecuteAsync(sql, args.ToArray());
updatedCount += rowsAffected;
_logger.LogInformation($"Chown chunk updated {rowsAffected} rows (chunk size {chunk.Count}).");
}
else
catch (Exception ex)
{
_logger.LogWarning($"Chown skipped: resource not found in DB (Uid={uid}) for target '{path}'");
_logger.LogError(ex, $"Error executing chown update chunk for path '{path}'.");
// continue with remaining chunks
}
}