diff --git a/devdocs/todo.txt b/devdocs/todo.txt index 30e1e471..f8f803d9 100644 --- a/devdocs/todo.txt +++ b/devdocs/todo.txt @@ -21,6 +21,7 @@ TODO: REFACTOR GetNoLogAsync function is used in many places redundantly when th TODO: REFACTOR biz objects have two creates, an async and sync one, WTF is that about? See if can make it just one async version. - https://docs.microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.1 +TODO: Seeder create widget is creating a new widgetbiz each loop iteration but create user is not, try both ways and performance test TODO: License.cs using httpclient directly albeit trying to do it right` - Change to use httpclientfactory injected service: diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index ddd555c4..eb68f5b6 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -423,12 +423,12 @@ namespace AyaNova { _newLog.LogWarning("BOOT: AYANOVA_PERMANENTLY_ERASE_DATABASE is true, dropping and recreating database"); Util.DbUtil.DropAndRecreateDbAsync(_newLog); - AySchema.CheckAndUpdate(dbContext, _newLog); + AySchema.CheckAndUpdateAsync(dbContext, _newLog); } //Check schema _newLog.LogDebug("BOOT: db schema check"); - AySchema.CheckAndUpdate(dbContext, _newLog); + AySchema.CheckAndUpdateAsync(dbContext, _newLog); //Check database integrity _newLog.LogDebug("BOOT: db integrity check"); diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index 0434db0d..841b37d9 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -1,5 +1,5 @@ using System; -using System.Text; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using AyaNova.Models; @@ -52,7 +52,7 @@ namespace AyaNova.Util //check and update schema - public static void CheckAndUpdate(AyContext context, ILogger logger) + public static async Task CheckAndUpdateAsync(AyContext context, ILogger logger) { ct = context; log = logger; @@ -63,14 +63,14 @@ namespace AyaNova.Util using (var command = ct.Database.GetDbConnection().CreateCommand()) { command.CommandText = "SELECT * FROM information_schema.tables WHERE table_name = 'aschemaversion'"; - ct.Database.OpenConnection(); - using (var result = command.ExecuteReader()) + await ct.Database.OpenConnectionAsync(); + using (var result = await command.ExecuteReaderAsync()) { if (result.HasRows) { aySchemaVersionExists = true; } - ct.Database.CloseConnection(); + await ct.Database.CloseConnectionAsync(); } } @@ -82,14 +82,14 @@ namespace AyaNova.Util //nope, no schema table, add it now and set to v1 using (var cm = ct.Database.GetDbConnection().CreateCommand()) { - ct.Database.OpenConnection(); + await ct.Database.OpenConnectionAsync(); cm.CommandText = "CREATE TABLE aschemaversion (schema INTEGER NOT NULL);"; - cm.ExecuteNonQuery(); + await cm.ExecuteNonQueryAsync(); cm.CommandText = "insert into aschemaversion (schema) values (1);"; - cm.ExecuteNonQuery(); + await cm.ExecuteNonQueryAsync(); - ct.Database.CloseConnection(); + await ct.Database.CloseConnectionAsync(); startingSchema = 1; currentSchema = 1; } @@ -101,19 +101,19 @@ namespace AyaNova.Util { log.LogDebug("Fetching current schema version"); cm.CommandText = "SELECT schema FROM aschemaversion;"; - ct.Database.OpenConnection(); - using (var result = cm.ExecuteReader()) + await ct.Database.OpenConnectionAsync(); + using (var result = await cm.ExecuteReaderAsync()) { if (result.HasRows) { - result.Read(); + await result.ReadAsync(); currentSchema = startingSchema = result.GetInt32(0); - ct.Database.CloseConnection(); + await ct.Database.CloseConnectionAsync(); log.LogDebug("AyaNova schema version is " + currentSchema.ToString()); } else { - ct.Database.CloseConnection(); + await ct.Database.CloseConnectionAsync(); throw new System.Exception("AyaNova->AySchema->CheckAndUpdate: Error reading schema version"); } } @@ -139,51 +139,51 @@ namespace AyaNova.Util LogUpdateMessage(log); //create aevent biz event log table - exec("CREATE TABLE aevent (id BIGSERIAL PRIMARY KEY, created timestamp not null, userid bigint not null," + + await ExecQueryAsync("CREATE TABLE aevent (id BIGSERIAL PRIMARY KEY, created timestamp not null, userid bigint not null," + "ayid bigint not null, aytype integer not null, ayevent integer not null, textra varchar(255))"); //SEARCH TABLES - exec("CREATE TABLE asearchdictionary (id BIGSERIAL PRIMARY KEY, word varchar(255) not null)"); + await ExecQueryAsync("CREATE TABLE asearchdictionary (id BIGSERIAL PRIMARY KEY, word varchar(255) not null)"); //Search dictionary words must be unique - exec("CREATE UNIQUE INDEX asearchdictionary_word_idx ON asearchdictionary (word);"); + await ExecQueryAsync("CREATE UNIQUE INDEX asearchdictionary_word_idx ON asearchdictionary (word);"); - exec("CREATE TABLE asearchkey (id BIGSERIAL PRIMARY KEY, wordid bigint not null REFERENCES asearchdictionary (id), objectid bigint not null, objecttype integer not null, inname bool not null)"); + await ExecQueryAsync("CREATE TABLE asearchkey (id BIGSERIAL PRIMARY KEY, wordid bigint not null REFERENCES asearchdictionary (id), objectid bigint not null, objecttype integer not null, inname bool not null)"); //create locale text tables - exec("CREATE TABLE alocale (id BIGSERIAL PRIMARY KEY, name varchar(255) not null, stock bool, cjkindex bool default false)"); + await ExecQueryAsync("CREATE TABLE alocale (id BIGSERIAL PRIMARY KEY, name varchar(255) not null, stock bool, cjkindex bool default false)"); //LOOKAT: I don't think this is doing anything: //exec("CREATE UNIQUE INDEX alocale_name_idx ON alocale (name)"); - exec("CREATE TABLE alocaleitem (id BIGSERIAL PRIMARY KEY, localeid bigint not null REFERENCES alocale (id), key text not null, display text not null)"); + await ExecQueryAsync("CREATE TABLE alocaleitem (id BIGSERIAL PRIMARY KEY, localeid bigint not null REFERENCES alocale (id), key text not null, display text not null)"); //LOOKAT: this is for what exactly?? // exec("CREATE INDEX alocaleitem_localeid_key_idx ON alocaleitem (localeid,key)"); //This seems more appropriate - exec("CREATE INDEX alocaleitem_localeid_key_display_idx ON alocaleitem (localeid,key, display)"); + await ExecQueryAsync("CREATE INDEX alocaleitem_localeid_key_display_idx ON alocaleitem (localeid,key, display)"); //Load the default LOCALES - AyaNova.Biz.PrimeData.PrimeLocales(); + await AyaNova.Biz.PrimeData.PrimeLocales(); //Add user table - exec("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, active bool not null, name varchar(255) not null, " + + await ExecQueryAsync("CREATE TABLE auser (id BIGSERIAL PRIMARY KEY, active bool not null, name varchar(255) not null, " + "login text not null, password text not null, salt text not null, roles integer not null, localeid bigint not null REFERENCES alocale (id), " + "dlkey text, dlkeyexpire timestamp, usertype integer not null, employeenumber varchar(255), notes text, customerid bigint, " + "headofficeid bigint, subvendorid bigint, customfields text, tags varchar(255) ARRAY)"); //Index for name fetching - exec("CREATE UNIQUE INDEX auser_name_id_idx ON auser (id, name);"); + await ExecQueryAsync("CREATE UNIQUE INDEX auser_name_id_idx ON auser (id, name);"); //Add user options table - exec("CREATE TABLE auseroptions (id BIGSERIAL PRIMARY KEY, " + + await ExecQueryAsync("CREATE TABLE auseroptions (id BIGSERIAL PRIMARY KEY, " + "userid bigint not null, timezoneoffset decimal(19,5) not null default 0, emailaddress text, uicolor int not null default 0)"); //Prime the db with the default MANAGER account - AyaNova.Biz.PrimeData.PrimeManagerAccount(ct); + await AyaNova.Biz.PrimeData.PrimeManagerAccount(ct); - setSchemaLevel(++currentSchema); + await SetSchemaLevelAsync(++currentSchema); } @@ -194,9 +194,9 @@ namespace AyaNova.Util LogUpdateMessage(log); //Add user table - exec("CREATE TABLE alicense (id BIGSERIAL PRIMARY KEY, dbid uuid, key text not null)"); + await ExecQueryAsync("CREATE TABLE alicense (id BIGSERIAL PRIMARY KEY, dbid uuid, key text not null)"); - setSchemaLevel(++currentSchema); + await SetSchemaLevelAsync(++currentSchema); } @@ -208,7 +208,7 @@ namespace AyaNova.Util //Add widget table //id, text, longtext, boolean, currency, - exec("CREATE TABLE awidget (id BIGSERIAL PRIMARY KEY, name varchar(255) not null, serial bigint not null," + + await ExecQueryAsync("CREATE TABLE awidget (id BIGSERIAL PRIMARY KEY, name varchar(255) not null, serial bigint not null," + "startdate timestamp, enddate timestamp, dollaramount decimal(19,5), active bool, roles int4, count integer," + "notes text, userid bigint null, customfields text, tags varchar(255) ARRAY)"); @@ -217,12 +217,12 @@ namespace AyaNova.Util //Compound index for name fetching - exec("CREATE UNIQUE INDEX awidget_name_id_idx ON awidget (id, name);"); + await ExecQueryAsync("CREATE UNIQUE INDEX awidget_name_id_idx ON awidget (id, name);"); //Index for tags - exec("CREATE INDEX awidget_tags ON AWIDGET using GIN(tags)"); + await ExecQueryAsync("CREATE INDEX awidget_tags ON AWIDGET using GIN(tags)"); - setSchemaLevel(++currentSchema); + await SetSchemaLevelAsync(++currentSchema); } @@ -232,15 +232,15 @@ namespace AyaNova.Util { LogUpdateMessage(log); - exec("CREATE TABLE afileattachment (id BIGSERIAL PRIMARY KEY, " + + await ExecQueryAsync("CREATE TABLE afileattachment (id BIGSERIAL PRIMARY KEY, " + "attachtoobjectid bigint not null, attachtoobjecttype integer not null, " + "storedfilename text not null, displayfilename text not null, contenttype text, notes text)"); //index required for ops that need to check if file already in db (delete, count refs etc) //LOOKAT: isn't this useless without the ID as well or is that not fetched? - exec("CREATE INDEX afileattachment_storedfilename_idx ON afileattachment (storedfilename);"); + await ExecQueryAsync("CREATE INDEX afileattachment_storedfilename_idx ON afileattachment (storedfilename);"); - setSchemaLevel(++currentSchema); + await SetSchemaLevelAsync(++currentSchema); } @@ -252,11 +252,11 @@ namespace AyaNova.Util { LogUpdateMessage(log); - exec("CREATE TABLE aopsjob (gid uuid PRIMARY KEY, name text not null, created timestamp not null, exclusive bool not null, " + + await ExecQueryAsync("CREATE TABLE aopsjob (gid uuid PRIMARY KEY, name text not null, created timestamp not null, exclusive bool not null, " + "startafter timestamp not null, jobtype integer not null, objectid bigint null, objecttype integer null, jobstatus integer not null, jobinfo text null)"); - exec("CREATE TABLE aopsjoblog (gid uuid PRIMARY KEY, jobid uuid not null REFERENCES aopsjob (gid), created timestamp not null, statustext text not null)"); + await ExecQueryAsync("CREATE TABLE aopsjoblog (gid uuid PRIMARY KEY, jobid uuid not null REFERENCES aopsjob (gid), created timestamp not null, statustext text not null)"); - setSchemaLevel(++currentSchema); + await SetSchemaLevelAsync(++currentSchema); } @@ -266,11 +266,11 @@ namespace AyaNova.Util { LogUpdateMessage(log); - exec("CREATE TABLE adatalistfilter (id BIGSERIAL PRIMARY KEY, userId bigint not null, name varchar(255) not null, public bool not null," + + await ExecQueryAsync("CREATE TABLE adatalistfilter (id BIGSERIAL PRIMARY KEY, userId bigint not null, name varchar(255) not null, public bool not null," + "listkey varchar(255) not null, filter text, sort text, UNIQUE(name))"); - exec("CREATE TABLE adatalisttemplate (id BIGSERIAL PRIMARY KEY, datalistkey text not null, template text, UNIQUE(datalistkey))"); - setSchemaLevel(++currentSchema); + await ExecQueryAsync("CREATE TABLE adatalisttemplate (id BIGSERIAL PRIMARY KEY, datalistkey text not null, template text, UNIQUE(datalistkey))"); + await SetSchemaLevelAsync(++currentSchema); } @@ -279,8 +279,8 @@ namespace AyaNova.Util if (currentSchema < 8) { LogUpdateMessage(log); - exec("CREATE TABLE atag (id BIGSERIAL PRIMARY KEY, name varchar(255) not null, refcount bigint not null, UNIQUE(name))"); - setSchemaLevel(++currentSchema); + await ExecQueryAsync("CREATE TABLE atag (id BIGSERIAL PRIMARY KEY, name varchar(255) not null, refcount bigint not null, UNIQUE(name))"); + await SetSchemaLevelAsync(++currentSchema); } @@ -291,9 +291,9 @@ namespace AyaNova.Util { LogUpdateMessage(log); - exec("CREATE TABLE aformcustom (id BIGSERIAL PRIMARY KEY, " + + await ExecQueryAsync("CREATE TABLE aformcustom (id BIGSERIAL PRIMARY KEY, " + "formkey varchar(255) not null, template text, UNIQUE(formkey))"); - setSchemaLevel(++currentSchema); + await SetSchemaLevelAsync(++currentSchema); } @@ -329,20 +329,20 @@ namespace AyaNova.Util - private static void setSchemaLevel(int nCurrentSchema) + private static async Task SetSchemaLevelAsync(int nCurrentSchema) { - exec("UPDATE aschemaversion SET schema=" + nCurrentSchema.ToString()); + await ExecQueryAsync("UPDATE aschemaversion SET schema=" + nCurrentSchema.ToString()); } //execute command query - private static void exec(string q) + private static async Task ExecQueryAsync(string q) { using (var cm = ct.Database.GetDbConnection().CreateCommand()) { - ct.Database.OpenConnection(); + await ct.Database.OpenConnectionAsync(); cm.CommandText = q; - cm.ExecuteNonQuery(); - ct.Database.CloseConnection(); + await cm.ExecuteNonQueryAsync(); + await ct.Database.CloseConnectionAsync(); } } diff --git a/server/AyaNova/util/DbUtil.cs b/server/AyaNova/util/DbUtil.cs index 3f17ee07..e915bb91 100644 --- a/server/AyaNova/util/DbUtil.cs +++ b/server/AyaNova/util/DbUtil.cs @@ -1,21 +1,9 @@ using System; using Microsoft.Extensions.Logging; using AyaNova.Models; -using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; - -using System.Linq; -using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.JsonPatch; -using EnumsNET; -using AyaNova.Util; -using AyaNova.Api.ControllerHelpers; -using AyaNova.Models; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; namespace AyaNova.Util { diff --git a/server/AyaNova/util/License.cs b/server/AyaNova/util/License.cs index 2b83841a..f945a346 100644 --- a/server/AyaNova/util/License.cs +++ b/server/AyaNova/util/License.cs @@ -519,7 +519,7 @@ namespace AyaNova.Core } //Can't install a trial into a non-empty db - if (ParsedNewKey.TrialLicense && ! DbUtil.DBIsEmptyAsync(ct, log)) + if (ParsedNewKey.TrialLicense && ! await DbUtil.DBIsEmptyAsync(ct, log)) { throw new ApplicationException("E1020 - Can't install a trial key into a non empty AyaNova database. Erase the database first."); } diff --git a/server/AyaNova/util/Seeder.cs b/server/AyaNova/util/Seeder.cs index f436596a..170fce73 100644 --- a/server/AyaNova/util/Seeder.cs +++ b/server/AyaNova/util/Seeder.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using System.Collections.Generic; using AyaNova.Models; using AyaNova.Biz; @@ -165,7 +166,7 @@ namespace AyaNova.Util watch = new Stopwatch(); watch.Start(); //await GenSeedWidgetAsync(log, 100); - GenSeedWidget(log, 100); + GenSeedWidgetAsync(log, 100); //PERF watch.Stop(); LogStatus(JobId, LogJob, log, $"100 Widgets seeded in {watch.ElapsedMilliseconds} ms"); @@ -227,7 +228,7 @@ namespace AyaNova.Util watch.Start(); //await GenSeedWidgetAsync(log, 500); - GenSeedWidget(log, 500); + GenSeedWidgetAsync(log, 500); //PERF watch.Stop(); LogStatus(JobId, LogJob, log, $"500 Widgets seeded in {watch.ElapsedMilliseconds} ms"); @@ -303,7 +304,7 @@ namespace AyaNova.Util watch = new Stopwatch(); watch.Start(); //await GenSeedWidgetAsync(log, 5000); - GenSeedWidget(log, 5000); + GenSeedWidgetAsync(log, 5000); //PERF watch.Stop(); @@ -380,7 +381,7 @@ namespace AyaNova.Util watch = new Stopwatch(); watch.Start(); //await GenSeedWidgetAsync(log, 20000); - GenSeedWidget(log, 20000); + GenSeedWidgetAsync(log, 20000); watch.Stop(); LogStatus(JobId, LogJob, log, $"20k Widgets seeded in {watch.ElapsedMilliseconds} ms"); #endregion genhuge @@ -509,15 +510,15 @@ namespace AyaNova.Util - public static void GenSeedUser(ILogger log, int count, AuthorizationRoles roles, UserType userType, decimal timeZoneOffset, + public static async Task GenSeedUser(ILogger log, int count, AuthorizationRoles roles, UserType userType, decimal timeZoneOffset, bool active = true, string login = null, string password = null, long localeId = 0) { if (localeId == 0) localeId = ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID; - using (var cct = ServiceProviderProvider.DBContext) + using (var ct = ServiceProviderProvider.DBContext) { - UserBiz Biz = UserBiz.GetBizInternal(cct); + UserBiz Biz = UserBiz.GetBizInternal(ct); //allow creation of not entirely ready users (missing client id or subcontractor vendor id etc) Biz.SeedOrImportRelaxedRulesMode = true; @@ -555,7 +556,7 @@ namespace AyaNova.Util u.UserOptions.EmailAddress = p.Email.Replace("gmail.com", "helloayanova.com").Replace("hotmail.com", "helloayanova.com").Replace("yahoo.com", "helloayanova.com"); u.UserOptions.TimeZoneOffset = timeZoneOffset; - var NewObject = Biz.Create(cct, u); + var NewObject = await Biz.CreateAsync(u); if (NewObject == null) { log.LogError($"Seeder::GenSeedUser error creating user {u.Name}\r\n" + Biz.GetErrorsAsString()); @@ -570,7 +571,7 @@ namespace AyaNova.Util ////////////////////////////////////////////////////// //Seed widget for testing // - public static void GenSeedWidget(ILogger log, int count) + public static async Task GenSeedWidgetAsync(ILogger log, int count) { var f = new Bogus.Faker(); @@ -610,7 +611,7 @@ namespace AyaNova.Util //var NewObject = Cached_WidgetBiz.CreateAsync(o).Result; //test without cached widgetbiz WidgetBiz biz = WidgetBiz.GetBizInternal(ServiceProviderProvider.DBContext); - var NewObject = biz.Create(o); + var NewObject = await biz.CreateAsync(o); if (NewObject == null) { log.LogError($"Seeder::GenSeedWidget error creating widget {o.Name}\r\n" + biz.GetErrorsAsString());