Improved tag processing code with fewer round trips to db

This commit is contained in:
2021-10-19 19:46:57 +00:00
parent 8a1e009850
commit 59153fbc78

View File

@@ -73,56 +73,77 @@ namespace AyaNova.Biz
public static async Task ProcessDeleteTagsInRepositoryAsync(AyContext ct, List<string> deleteTags)
{
if (deleteTags.Count == 0) return;
var existing = await ct.Tag.Where(x => deleteTags.Contains(x.Name)).ToListAsync();
foreach (string s in deleteTags)
{
bool bDone = false;
//Keep on trying until there is success
//this allows for concurrency issues
//I considered a circuit breaker / timeout here, but theoretically it should not be an issue
//at some point it should not be a concurrency issue anymore
//And this is not critical functionality requiring a transaction and locking
//TODO: Move this into a stored procedure just like search keyword processing for efficiency
//it's likely not going to be an issue but still, it's not right to do it this way
do
var t = existing.FirstOrDefault(x => x.Name == s);
if (t != null)
{
//START: Get tag word and concurrency token and count
var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
//if not present, then nothing to do
if (ExistingTag != null)
{
//No longer needed?
if (ExistingTag.RefCount == 1)
{
ct.Remove(ExistingTag);
}
if (t.RefCount < 2)//catch any that fell through the cracks and are maybe zero or negative event
ct.Remove(t);
else
{
//Decrement the refcount
ExistingTag.RefCount -= 1;
t.RefCount -= 1;
}
try
{
}
await ct.SaveChangesAsync();
bDone = true;
}
catch (Exception ex) when (ex is DbUpdateConcurrencyException)//allow for possible other types
{
//allow others to flow past
// string sss = ex.ToString();
}
}else{
bDone=true;
}
} while (bDone == false);
}
#region OLD SLOW METHOD FOR REFERENCE IN CASE CONCURRENCY EXCEPTIONS
// foreach (string s in deleteTags)
// {
// bool bDone = false;
// //Keep on trying until there is success
// //this allows for concurrency issues
// //I considered a circuit breaker / timeout here, but theoretically it should not be an issue
// //at some point it should not be a concurrency issue anymore
// //And this is not critical functionality requiring a transaction and locking
// do
// {
// //START: Get tag word and concurrency token and count
// var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
// //if not present, then nothing to do
// if (ExistingTag != null)
// {
// //No longer needed?
// if (ExistingTag.RefCount == 1)
// {
// ct.Remove(ExistingTag);
// }
// else
// {
// //Decrement the refcount
// ExistingTag.RefCount -= 1;
// }
// try
// {
// await ct.SaveChangesAsync();
// bDone = true;
// }
// catch (Exception ex) when (ex is DbUpdateConcurrencyException)//allow for possible other types
// {
// //allow others to flow past
// // string sss = ex.ToString();
// }
// }
// else
// {
// bDone = true;
// }
// } while (bDone == false);
// }
#endregion old slow method
}
public static async Task ProcessUpdateTagsInRepositoryAsync(AyContext ct, List<string> newTags, List<string> originalTags = null)
{
//todo: Recode this as a procedure like search indexing or at least a direct sql call
//just in case no new tags are present which could mean a user removed all tags from a record so this
//needs to proceed with the code below even if newTags is null as long as originalTags isn't also null
if (newTags == null) newTags = new List<string>();
@@ -147,41 +168,64 @@ namespace AyaNova.Biz
addTags = newTags;
}
#region OLD SLOW METHOD FOR REFERENCE IN CASE CONCURRENCY EXCEPTIONS
// //ADD / INCREMENT TAGS
// //one by one method
// foreach (string s in addTags)
// {
// bool bDone = false;
// //Keep on trying until there is success
// //this allows for concurrency issues
// //I considered a circuit breaker / timeout here, but theoretically it should not be an issue
// //at some point it should not be a concurrency issue anymore
// do
// {
// //START: Get tag word and concurrency token and count
// var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
// //if not present, then add it with a count of 0
// if (ExistingTag == null)
// {
// await ct.Tag.AddAsync(new Tag() { Name = s, RefCount = 1 });
// }
// else
// {
// //Update the refcount
// ExistingTag.RefCount += 1;
// }
// try
// {
// await ct.SaveChangesAsync();
// bDone = true;
// }
// catch (Exception ex) when (ex is DbUpdateConcurrencyException)//this allows for other types
// {
// Console.WriteLine("TagBiz::Exception udring update tags");
// //allow others to flow past
// //string sss = ex.ToString();
// }
// } while (bDone == false);
// }
#endregion old slow method
//ADD / INCREMENT TAGS
var existing = await ct.Tag.Where(x => addTags.Contains(x.Name)).ToListAsync();
foreach (string s in addTags)
{
bool bDone = false;
//Keep on trying until there is success
//this allows for concurrency issues
//I considered a circuit breaker / timeout here, but theoretically it should not be an issue
//at some point it should not be a concurrency issue anymore
do
var t = existing.FirstOrDefault(x => x.Name == s);
if (t != null)
{
//START: Get tag word and concurrency token and count
var ExistingTag = await ct.Tag.FirstOrDefaultAsync(z => z.Name == s);
//if not present, then add it with a count of 0
if (ExistingTag == null)
{
await ct.Tag.AddAsync(new Tag() { Name = s, RefCount = 1 });
t.RefCount += 1;
}
else
{
//Update the refcount
ExistingTag.RefCount += 1;
ct.Tag.Add(new Tag() { Name = s, RefCount = 1 });
}
}
try
{
await ct.SaveChangesAsync();
bDone = true;
}
catch (Exception ex) when (ex is DbUpdateConcurrencyException)//this allows for other types
{
//allow others to flow past
//string sss = ex.ToString();
}
} while (bDone == false);
}
//DELETE TAGS
await ProcessDeleteTagsInRepositoryAsync(ct, deleteTags);