Compare commits

..

5 Commits

Author SHA1 Message Date
Michael Green
308338580d Cache objects from database in memory to improve performance (#151)
* IGDB objects are now cached in memory

* Completed caching of PlatformMaps
2023-10-10 23:59:52 +11:00
Michael Green
49784dc325 Removed import block from LibraryScan (#150) 2023-10-10 11:00:13 +11:00
Michael Green
fc09db60ab Updated the about page (#137) 2023-09-27 21:58:43 +10:00
Michael Green
906456782a Logs page now has paging (#136) 2023-09-25 21:22:27 +10:00
Michael Green
d6d6a5d808 Resolved blocked service UI bug (#134) 2023-09-24 13:59:01 +10:00
11 changed files with 210 additions and 67 deletions

View File

@@ -4,6 +4,7 @@ using System.Reflection;
using gaseous_tools; using gaseous_tools;
using IGDB; using IGDB;
using IGDB.Models; using IGDB.Models;
using Microsoft.Extensions.Caching.Memory;
namespace gaseous_server.Classes.Metadata namespace gaseous_server.Classes.Metadata
{ {
@@ -16,14 +17,32 @@ namespace gaseous_server.Classes.Metadata
Expired Expired
} }
private static Dictionary<string, MemoryCacheObject> ObjectCache = new Dictionary<string, MemoryCacheObject>();
public static CacheStatus GetCacheStatus(string Endpoint, string Slug) public static CacheStatus GetCacheStatus(string Endpoint, string Slug)
{ {
return _GetCacheStatus(Endpoint, "slug", Slug); CacheClean();
if (ObjectCache.ContainsKey(Endpoint + Slug))
{
return CacheStatus.Current;
}
else
{
return _GetCacheStatus(Endpoint, "slug", Slug);
}
} }
public static CacheStatus GetCacheStatus(string Endpoint, long Id) public static CacheStatus GetCacheStatus(string Endpoint, long Id)
{ {
return _GetCacheStatus(Endpoint, "id", Id); CacheClean();
if (ObjectCache.ContainsKey(Endpoint + Id))
{
return CacheStatus.Current;
}
else
{
return _GetCacheStatus(Endpoint, "id", Id);
}
} }
public static CacheStatus GetCacheStatus(DataRow Row) public static CacheStatus GetCacheStatus(DataRow Row)
@@ -164,6 +183,21 @@ namespace gaseous_server.Classes.Metadata
{ {
string Endpoint = EndpointType.GetType().Name; string Endpoint = EndpointType.GetType().Name;
if (ObjectCache.ContainsKey(Endpoint + SearchValue))
{
MemoryCacheObject cacheObject = ObjectCache[Endpoint + SearchValue];
if (cacheObject.ExpiryTime < DateTime.UtcNow)
{
// object has expired, remove it
ObjectCache.Remove(Endpoint + SearchValue);
}
else
{
// object is valid, return it
return (T)cacheObject.Object;
}
}
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField; string sql = "SELECT * FROM " + Endpoint + " WHERE " + SearchField + " = @" + SearchField;
@@ -181,7 +215,11 @@ namespace gaseous_server.Classes.Metadata
else else
{ {
DataRow dataRow = dt.Rows[0]; DataRow dataRow = dt.Rows[0];
return BuildCacheObject<T>(EndpointType, dataRow); object returnObject = BuildCacheObject<T>(EndpointType, dataRow);
ObjectCache.Add(Endpoint + SearchValue, new MemoryCacheObject{
Object = returnObject
});
return (T)returnObject;
} }
} }
@@ -380,6 +418,31 @@ namespace gaseous_server.Classes.Metadata
return EndpointType; return EndpointType;
} }
private static void CacheClean()
{
Dictionary<string, MemoryCacheObject> workCache = ObjectCache;
foreach (KeyValuePair<string, MemoryCacheObject> objectCache in workCache)
{
if (objectCache.Value.ExpiryTime < DateTime.UtcNow)
{
ObjectCache.Remove(objectCache.Key);
}
}
}
private class MemoryCacheObject
{
public object Object { get; set; }
public DateTime CreationTime { get; } = DateTime.UtcNow;
public DateTime ExpiryTime
{
get
{
return CreationTime.AddMinutes(60);
}
}
}
} }
} }

View File

@@ -13,9 +13,9 @@ namespace gaseous_server.Controllers
{ {
[HttpGet] [HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public List<Logging.LogItem> Logs() public List<Logging.LogItem> Logs(long? StartIndex, int PageNumber = 1, int PageSize = 100)
{ {
return Logging.GetLogs(); return Logging.GetLogs(StartIndex, PageNumber, PageSize);
} }
} }
} }

View File

@@ -15,6 +15,8 @@ namespace gaseous_server.Models
{ {
public class PlatformMapping public class PlatformMapping
{ {
private static Dictionary<string, PlatformMapItem> PlatformMapCache = new Dictionary<string, PlatformMapItem>();
/// <summary> /// <summary>
/// Updates the platform map from the embedded platform map resource /// Updates the platform map from the embedded platform map resource
/// </summary> /// </summary>
@@ -98,7 +100,15 @@ namespace gaseous_server.Models
List<PlatformMapItem> platformMaps = new List<PlatformMapItem>(); List<PlatformMapItem> platformMaps = new List<PlatformMapItem>();
foreach (DataRow row in data.Rows) foreach (DataRow row in data.Rows)
{ {
platformMaps.Add(BuildPlatformMapItem(row)); long mapId = (long)row["Id"];
if (PlatformMapCache.ContainsKey(mapId.ToString()))
{
platformMaps.Add(PlatformMapCache[mapId.ToString()]);
}
else
{
platformMaps.Add(BuildPlatformMapItem(row));
}
} }
platformMaps.Sort((x, y) => x.IGDBName.CompareTo(y.IGDBName)); platformMaps.Sort((x, y) => x.IGDBName.CompareTo(y.IGDBName));
@@ -109,23 +119,30 @@ namespace gaseous_server.Models
public static PlatformMapItem GetPlatformMap(long Id) public static PlatformMapItem GetPlatformMap(long Id)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); if (PlatformMapCache.ContainsKey(Id.ToString()))
string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 0)
{ {
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]); return PlatformMapCache[Id.ToString()];
return platformMap;
} }
else else
{ {
Exception exception = new Exception("Platform Map Id " + Id + " does not exist."); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
Logging.Log(Logging.LogType.Critical, "Platform Map", "Platform Map Id " + Id + " does not exist.", exception); string sql = "SELECT * FROM PlatformMap WHERE Id = @Id";
throw exception; Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("Id", Id);
DataTable data = db.ExecuteCMD(sql, dbDict);
if (data.Rows.Count > 0)
{
PlatformMapItem platformMap = BuildPlatformMapItem(data.Rows[0]);
return platformMap;
}
else
{
Exception exception = new Exception("Platform Map Id " + Id + " does not exist.");
Logging.Log(Logging.LogType.Critical, "Platform Map", "Platform Map Id " + Id + " does not exist.", exception);
throw exception;
}
} }
} }
@@ -218,6 +235,11 @@ namespace gaseous_server.Models
db.ExecuteCMD(sql, dbDict); db.ExecuteCMD(sql, dbDict);
} }
} }
if (PlatformMapCache.ContainsKey(item.IGDBId.ToString()))
{
PlatformMapCache.Remove(item.IGDBId.ToString());
}
} }
static PlatformMapItem BuildPlatformMapItem(DataRow row) static PlatformMapItem BuildPlatformMapItem(DataRow row)
@@ -321,6 +343,15 @@ namespace gaseous_server.Models
}; };
mapItem.Bios = bioss; mapItem.Bios = bioss;
if (PlatformMapCache.ContainsKey(IGDBId.ToString()))
{
PlatformMapCache[IGDBId.ToString()] = mapItem;
}
else
{
PlatformMapCache.Add(IGDBId.ToString(), mapItem);
}
return mapItem; return mapItem;
} }

View File

@@ -182,9 +182,8 @@ ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
}) })
); );
ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem( ProcessQueue.QueueItems.Add(new ProcessQueue.QueueItem(
ProcessQueue.QueueItemType.LibraryScan, 30, new List<ProcessQueue.QueueItemType> ProcessQueue.QueueItemType.LibraryScan, 1440, new List<ProcessQueue.QueueItemType>
{ {
ProcessQueue.QueueItemType.TitleIngestor,
ProcessQueue.QueueItemType.OrganiseLibrary ProcessQueue.QueueItemType.OrganiseLibrary
}) })
); );

View File

@@ -36,7 +36,7 @@ namespace gaseous_server
List<ProcessQueue.QueueItem> ActiveList = new List<ProcessQueue.QueueItem>(); List<ProcessQueue.QueueItem> ActiveList = new List<ProcessQueue.QueueItem>();
ActiveList.AddRange(ProcessQueue.QueueItems); ActiveList.AddRange(ProcessQueue.QueueItems);
foreach (ProcessQueue.QueueItem qi in ActiveList) { foreach (ProcessQueue.QueueItem qi in ActiveList) {
if (CheckProcessBlockList(qi) == true) { if (CheckIfProcessIsBlockedByOthers(qi) == false) {
qi.BlockedState(false); qi.BlockedState(false);
if (DateTime.UtcNow > qi.NextRunTime || qi.Force == true) if (DateTime.UtcNow > qi.NextRunTime || qi.Force == true)
{ {
@@ -69,30 +69,24 @@ namespace gaseous_server
_timer?.Dispose(); _timer?.Dispose();
} }
private bool CheckProcessBlockList(ProcessQueue.QueueItem queueItem) private bool CheckIfProcessIsBlockedByOthers(ProcessQueue.QueueItem queueItem)
{ {
if (queueItem.Blocks.Count > 0) foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems)
{ {
foreach (ProcessQueue.QueueItem qi in ProcessQueue.QueueItems) if (qi.ItemState == ProcessQueue.QueueItemState.Running) {
{ // other service is running, check if queueItem is blocked by it
if ( if (
( qi.Blocks.Contains(queueItem.ItemType) ||
queueItem.Blocks.Contains(qi.ItemType) || qi.Blocks.Contains(ProcessQueue.QueueItemType.All)
queueItem.Blocks.Contains(ProcessQueue.QueueItemType.All) )
) &&
qi.ItemState == ProcessQueue.QueueItemState.Running
)
{ {
return false; Console.WriteLine(queueItem.ItemType.ToString() + " is blocked by " + qi.ItemType.ToString());
return true;
} }
} }
}
return true; return false;
}
else
{
return true;
}
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -4,13 +4,17 @@
<table style="width: 100%;"> <table style="width: 100%;">
<tr> <tr>
<th>Home Page</th> <th style="width: 20%;">Home Page</th>
<td><a href="https://github.com/gaseous-project/gaseous-server" class="romlink">https://github.com/gaseous-project/gaseous-server</a></td> <td><a href="https://github.com/gaseous-project/gaseous-server" class="romlink">https://github.com/gaseous-project/gaseous-server</a></td>
</tr> </tr>
<tr> <tr>
<th>Bugs and Feature Requests</th> <th>Bugs and Feature Requests</th>
<td><a href="https://github.com/gaseous-project/gaseous-server/issues" class="romlink">https://github.com/gaseous-project/gaseous-server/issues</a></td> <td><a href="https://github.com/gaseous-project/gaseous-server/issues" class="romlink">https://github.com/gaseous-project/gaseous-server/issues</a></td>
</tr> </tr>
<tr>
<th>Join our Discord</th>
<td><a href="https://discord.gg/Nhu7wpT3k4" class="romlink">https://discord.gg/Nhu7wpT3k4</a></td>
</tr>
<tr> <tr>
<th>Server Version</th> <th>Server Version</th>
<td id="settings_appversion"></td> <td id="settings_appversion"></td>
@@ -19,6 +23,18 @@
<th>Database Schema Version</th> <th>Database Schema Version</th>
<td id="settings_dbversion"></td> <td id="settings_dbversion"></td>
</tr> </tr>
<tr>
<td colspan="2">
<h3>Projects That Make Gaseous Possible</h3>
</td>
</tr>
<tr>
<td style="text-align: center;"><a href="https://github.com/EmulatorJS/EmulatorJS" target="_blank"><img src="/images/EmulatorJS.png" style="height: 36px;" /></a></td>
<td>
The EmulatorJS Project<br />
<a href="https://github.com/EmulatorJS/EmulatorJS" target="_blank" class="romlink">https://github.com/EmulatorJS/EmulatorJS</a>
</td>
</tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
<h3>Data Sources</h2> <h3>Data Sources</h2>
@@ -26,11 +42,12 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td style="text-align: center;">
<a href="https://www.igdb.com/" target="_blank"><img src="/images/IGDB_logo.svg" style="filter: invert(100%); height: 36px;" /></a> <a href="https://www.igdb.com/" target="_blank"><img src="/images/IGDB_logo.svg" style="filter: invert(100%); height: 36px;" /></a>
</td> </td>
<td> <td>
The Internet Game Database The Internet Game Database<br />
<a href="https://www.igdb.com/" target="_blank" class="romlink">https://www.igdb.com/</a>
</td> </td>
</tr> </tr>
<tr> <tr>
@@ -39,19 +56,21 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td style="text-align: center;">
<a href="https://www.tosecdev.org/" target="_blank"><img src="/images/TOSEC_logo.gif" style="height: 36px;" /></a> <a href="https://www.tosecdev.org/" target="_blank"><img src="/images/TOSEC_logo.gif" style="height: 36px;" /></a>
</td> </td>
<td> <td>
The Old School Emulation Center The Old School Emulation Center<br />
<a href="https://www.tosecdev.org/" target="_blank" class="romlink">https://www.tosecdev.org/</a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td style="text-align: center;">
<a href="https://www.progettosnaps.net/index.php" target="_blank"><img src="/images/ProgettoSnaps.gif" style="height: 36px;" /></a> <a href="https://www.progettosnaps.net/index.php" target="_blank"><img src="/images/ProgettoSnaps.gif" style="height: 36px;" /></a>
</td> </td>
<td> <td>
Progetto-Snaps Progetto-Snaps<br />
<a href="https://www.progettosnaps.net/index.php" target="_blank" class="romlink">https://www.progettosnaps.net/index.php</a>
</td> </td>
</tr> </tr>
</table> </table>

View File

@@ -7,30 +7,53 @@
</table> </table>
<div style="width: 960px; text-align: center;">
<button value="Load More" onclick="loadLogs(lastStartIndex, currentPage);">Load More</button>
</div>
<script type="text/javascript"> <script type="text/javascript">
function loadLogs() { var lastStartIndex = 0;
var currentPage = 1;
function loadLogs(StartIndex, PageNumber) {
var apiQuery = '';
if (StartIndex && PageNumber) {
currentPage += 1;
apiQuery = '?StartIndex=' + StartIndex + '&PageNumber=' + PageNumber;
} else {
currentPage = 1;
}
ajaxCall( ajaxCall(
'/api/v1/Logs', '/api/v1/Logs' + apiQuery,
'GET', 'GET',
function (result) { function (result) {
var newTable = document.getElementById('settings_events_table'); var newTable = document.getElementById('settings_events_table');
newTable.innerHTML = ''; if (currentPage == 1) {
newTable.appendChild( newTable.innerHTML = '';
createTableRow(
true, newTable.appendChild(
[ createTableRow(
['Event Time', 'logs_table_cell_150px'], true,
['Severity', 'logs_table_cell_150px'], [
'Process', //'Id',
'Message' ['Event Time', 'logs_table_cell_150px'],
], ['Severity', 'logs_table_cell_150px'],
'', 'Process',
'' 'Message'
) ],
); '',
''
)
);
}
for (var i = 0; i < result.length; i++) { for (var i = 0; i < result.length; i++) {
lastStartIndex = result[i].id;
var newRow = [ var newRow = [
//result[i].id,
moment(result[i].eventTime).fromNow(), moment(result[i].eventTime).fromNow(),
result[i].eventType, result[i].eventType,
result[i].process, result[i].process,

View File

@@ -64,7 +64,7 @@
var itemStateName; var itemStateName;
var itemLastStart; var itemLastStart;
if (result[i].IsBlocked == false) { if (result[i].isBlocked == false) {
switch (result[i].itemState) { switch (result[i].itemState) {
case 'NeverStarted': case 'NeverStarted':
itemStateName = "Never started"; itemStateName = "Never started";

View File

@@ -111,17 +111,30 @@ namespace gaseous_tools
File.AppendAllText(Config.LogFilePath, TraceOutput); File.AppendAllText(Config.LogFilePath, TraceOutput);
} }
static public List<LogItem> GetLogs() static public List<LogItem> GetLogs(long? StartIndex, int PageNumber = 1, int PageSize = 100)
{ {
Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString); Database db = new gaseous_tools.Database(Database.databaseType.MySql, Config.DatabaseConfiguration.ConnectionString);
string sql = "SELECT * FROM ServerLogs ORDER BY Id DESC"; string sql = "";
DataTable dataTable = db.ExecuteCMD(sql); if (StartIndex == null)
{
sql = "SELECT * FROM ServerLogs ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
}
else
{
sql = "SELECT * FROM ServerLogs WHERE Id < @StartIndex ORDER BY Id DESC LIMIT @PageSize OFFSET @PageNumber;";
}
Dictionary<string, object> dbDict = new Dictionary<string, object>();
dbDict.Add("StartIndex", StartIndex);
dbDict.Add("PageNumber", (PageNumber - 1) * PageSize);
dbDict.Add("PageSize", PageSize);
DataTable dataTable = db.ExecuteCMD(sql, dbDict);
List<LogItem> logs = new List<LogItem>(); List<LogItem> logs = new List<LogItem>();
foreach (DataRow row in dataTable.Rows) foreach (DataRow row in dataTable.Rows)
{ {
LogItem log = new LogItem LogItem log = new LogItem
{ {
Id = (long)row["Id"],
EventTime = DateTime.Parse(((DateTime)row["EventTime"]).ToString("yyyy-MM-ddThh:mm:ss") + 'Z'), EventTime = DateTime.Parse(((DateTime)row["EventTime"]).ToString("yyyy-MM-ddThh:mm:ss") + 'Z'),
EventType = (LogType)row["EventType"], EventType = (LogType)row["EventType"],
Process = (string)row["Process"], Process = (string)row["Process"],
@@ -145,6 +158,7 @@ namespace gaseous_tools
public class LogItem public class LogItem
{ {
public long Id { get; set; }
public DateTime EventTime { get; set; } public DateTime EventTime { get; set; }
public LogType? EventType { get; set; } public LogType? EventType { get; set; }
public string Process { get; set; } = ""; public string Process { get; set; } = "";