Raven DB DocumentStore - throws out of memory exception - out-of-memory

I have code like this:
public bool Set(IEnumerable<WhiteForest.Common.Entities.Projections.RequestProjection> requests)
{
var documentSession = _documentStore.OpenSession();
//{
try
{
foreach (var request in requests)
{
documentSession.Store(request);
}
//requests.AsParallel().ForAll(x => documentSession.Store(x));
documentSession.SaveChanges();
documentSession.Dispose();
return true;
}
catch (Exception e)
{
_log.LogDebug("Exception in RavenRequstRepository - Set. Exception is [{0}]", e.ToString());
return false;
}
//}
}
This code gets called many times. After i get to around 50,000 documents that have passed through it i get an OutOfMemoryException.
Any idea why ? perhaps after a while i need to declare a new DocumentStore ?
thank you
**
UPDATE:
**
I ended up using the Batch/Patch API to perform the update I needed.
You can see the discussion here: https://groups.google.com/d/topic/ravendb/3wRT9c8Y-YE/discussion
Basically since i only needed to update 1 property on my objects, and after considering ayendes comments about re-serializing all the objects back to JSON, i did something like this:
internal void Patch()
{
List<string> docIds = new List<string>() { "596548a7-61ef-4465-95bc-b651079f4888", "cbbca8d5-be45-4e0d-91cf-f4129e13e65e" };
using (var session = _documentStore.OpenSession())
{
session.Advanced.DatabaseCommands.Batch(GenerateCommands(docIds));
}
}
private List<ICommandData> GenerateCommands(List<string> docIds )
{
List<ICommandData> retList = new List<ICommandData>();
foreach (var item in docIds)
{
retList.Add(new PatchCommandData()
{
Key = item,
Patches = new[] { new Raven.Abstractions.Data.PatchRequest () {
Name = "Processed",
Type = Raven.Abstractions.Data.PatchCommandType.Set,
Value = new RavenJValue(true)
}}});
}
return retList;
}
Hope this helps ...
Thanks alot.

I just did this for my current project. I chunked the data into pieces and saved each chunk in a new session. This may work for you, too.
Note, this example shows chunking by 1024 documents at a time, but needing at least 2000 before we decide it's worth chunking. So far, my inserts got the best performance with a chunk size of 4096. I think that's because my documents are relatively small.
internal static void WriteObjectList<T>(List<T> objectList)
{
int numberOfObjectsThatWarrantChunking = 2000; // Don't bother chunking unless we have at least this many objects.
if (objectList.Count < numberOfObjectsThatWarrantChunking)
{
// Just write them all at once.
using (IDocumentSession ravenSession = GetRavenSession())
{
objectList.ForEach(x => ravenSession.Store(x));
ravenSession.SaveChanges();
}
return;
}
int numberOfDocumentsPerSession = 1024; // Chunk size
List<List<T>> objectListInChunks = new List<List<T>>();
for (int i = 0; i < objectList.Count; i += numberOfDocumentsPerSession)
{
objectListInChunks.Add(objectList.Skip(i).Take(numberOfDocumentsPerSession).ToList());
}
Parallel.ForEach(objectListInChunks, listOfObjects =>
{
using (IDocumentSession ravenSession = GetRavenSession())
{
listOfObjects.ForEach(x => ravenSession.Store(x));
ravenSession.SaveChanges();
}
});
}
private static IDocumentSession GetRavenSession()
{
return _ravenDatabase.OpenSession();
}

Are you trying to save it all in one call?
The DocumentSession need to turn all of the objects that you pass it into a single request to the server. That means that it may allocate a lot of memory for the write to the server.
Usually we recommend on batches of about 1,024 items in you are doing bulks saves.

DocumentStore is a disposable class, so I worked around this problem by disposing the instance after each chunk. I highly doubt this is the most efficient way to run operations, but it will prevent significant memory overhead from happening.
I was running a sort of "delete all" operation like so. You can see the using blocks disposing both the DocumentStore and the IDocumentSession objects after each chunk.
static DocumentStore GetDataStore()
{
DocumentStore ds = new DocumentStore
{
DefaultDatabase = "test",
Url = "http://localhost:8080"
};
ds.Initialize();
return ds;
}
static IDocumentSession GetDbInstance(DocumentStore ds)
{
return ds.OpenSession();
}
static void Main(string[] args)
{
do
{
using (var ds = GetDataStore())
using (var db = GetDbInstance(ds))
{
//The `Take` operation will cap out at 1,024 by default, per Raven documentation
var list = db.Query<MyClass>().Skip(deleteSum).Take(5000).ToList();
deleteCount = list.Count;
deleteSum += deleteCount;
foreach (var item in list)
{
db.Delete(item);
}
db.SaveChanges();
list.Clear();
}
} while (deleteCount > 0);
}

Related

I have a "Upload Record" PXAction to load records to grid and release these records

I have a custom PXbutton called UploadRecords, when I click this button I should populate the grid with records and release the records.
Release Action is pressed in the UploadRecords action delegate. The problem I get with this code is, the code here function properly for less records by release action but when passes thousands of records to release, it takes huge time(> 30 min.) and show the error like Execution timeout.
suggest me to avoid more execution time and release the records fastly.
namespace PX.Objects.AR
{
public class ARPriceWorksheetMaint_Extension : PXGraphExtension<ARPriceWorksheetMaint>
{
//public class string_R112 : Constant<string>
//{
// public string_R112()
// : base("4E5CCAFC-0957-4DB3-A4DA-2A24EA700047")
// {
// }
//}
public class string_R112 : Constant<string>
{
public string_R112()
: base("EA")
{
}
}
public PXSelectJoin<InventoryItem, InnerJoin<CSAnswers, On<InventoryItem.noteID, Equal<CSAnswers.refNoteID>>,
LeftJoin<INItemCost, On<InventoryItem.inventoryID, Equal<INItemCost.inventoryID>>>>,
Where<InventoryItem.salesUnit, Equal<string_R112>>> records;
public PXAction<ARPriceWorksheet> uploadRecord;
[PXUIField(DisplayName = "Upload Records", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXButton]
public IEnumerable UploadRecord(PXAdapter adapter)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in records.Select())
{
InventoryItem invItem = (InventoryItem)res;
INItemCost itemCost = (INItemCost)res;
CSAnswers csAnswer = (CSAnswers)res;
ARPriceWorksheetDetail gridDetail = new ARPriceWorksheetDetail();
gridDetail.PriceType = PriceTypeList.CustomerPriceClass;
gridDetail.PriceCode = csAnswer.AttributeID;
gridDetail.AlternateID = "";
gridDetail.InventoryID = invItem.InventoryID;
gridDetail.Description = invItem.Descr;
gridDetail.UOM = "EA";
gridDetail.SiteID = 6;
InventoryItemExt invExt = PXCache<InventoryItem>.GetExtension<InventoryItemExt>(invItem);
decimal y;
if (decimal.TryParse(csAnswer.Value, out y))
{
y = decimal.Parse(csAnswer.Value);
}
else
y = decimal.Parse(csAnswer.Value.Replace(" ", ""));
gridDetail.CurrentPrice = y; //(invExt.UsrMarketCost ?? 0m) * (Math.Round(y / 100, 2));
gridDetail.PendingPrice = y; // (invExt.UsrMarketCost ?? 0m)* (Math.Round( y/ 100, 2));
gridDetail.TaxID = null;
Base.Details.Update(gridDetail);
}
ts.Complete();
}
Base.Document.Current.Hold = false;
using (PXTransactionScope ts = new PXTransactionScope())
{
Base.Release.Press();
ts.Complete();
}
List<ARPriceWorksheet> lst = new List<ARPriceWorksheet>
{
Base.Document.Current
};
return lst;
}
protected void ARPriceWorksheet_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARPriceWorksheet)e.Row;
uploadRecord.SetEnabled(row.Status != SPWorksheetStatus.Released);
}
}
}
First, Do you need them all to be in a single transaction scope? This would revert all changes if there is an exception in any. If you need to have them all committed without any errors rather than each record, you would have to perform the updates this way.
I would suggest though moving your process to a custom processing screen. This way you can load the records, select one or many, and use the processing engine built into Acumatica to handle the process, rather than a single button click action. Here is an example: https://www.acumatica.com/blog/creating-custom-processing-screens-in-acumatica/
Based on the feedback that it must be all in a single transaction scope and thousands of records, I can only see two optimizations that may assist. First is increasing the Timeout as explained in this blog post. https://acumaticaclouderp.blogspot.com/2017/12/acumatica-snapshots-uploading-and.html
Next I would load all records into memory first and then loop through them with a ToList(). That might save you time as it should pull all records at once rather than once for each record.
going from
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in records.Select())
to
var recordList = records.Select().ToList();
foreach (PXResult<InventoryItem, CSAnswers, INItemCost> res in recordList)

ServiceStack OrmLite - Elegant way to handle SQL Server Connection Drops

We are currently using ORMLite and it is working really well.
One of the places that we are using it is for running large batch processes.
These processes run a single large batch all within a single transaction, if there are any errors then it rolls back the transaction and then it needs to be run again.
Is there a way that something like a connection drop(which could be very quick) could be better handled and that it could then, just re-establish the connection and then re-continue from there?
The only thing that's resembles something close to what you're after is using a Custom OrmLite Exec Fitler which you can use to inject your own custom Execution strategy.
The example on OrmLite's home page shows an example of using an Exec filter to execute each query 3 times:
public class ReplayOrmLiteExecFilter : OrmLiteExecFilter
{
public int ReplayTimes { get; set; }
public override T Exec<T>(IDbConnection dbConn, Func<IDbCommand, T> filter)
{
var holdProvider = OrmLiteConfig.DialectProvider;
var dbCmd = CreateCommand(dbConn);
try
{
var ret = default(T);
for (var i = 0; i < ReplayTimes; i++)
{
ret = filter(dbCmd);
}
return ret;
}
finally
{
DisposeCommand(dbCmd);
OrmLiteConfig.DialectProvider = holdProvider;
}
}
}
OrmLiteConfig.ExecFilter = new ReplayOrmLiteExecFilter { ReplayTimes = 3 };
using (var db = OpenDbConnection())
{
db.DropAndCreateTable<PocoTable>();
db.Insert(new PocoTable { Name = "Multiplicity" });
var rowsInserted = db.Count<PocoTable>(x => x.Name == "Multiplicity"); //3
}
But it uses the same IDbConnection, i.e. it doesn't create a new DB Connection.

Web Api Help Page XML comments from more than 1 files

I have different plugins in my Web api project with their own XML docs, and have one centralized Help page, but the problem is that Web Api's default Help Page only supports single documentation file
new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/Documentation.xml"))
How is it possible to load config from different files? I wan to do sth like this:
new XmlDocumentationProvider("PluginsFolder/*.xml")
You can modify the installed XmlDocumentationProvider at Areas\HelpPage to do something like following:
Merge multiple Xml document files into a single one:
Example code(is missing some error checks and validation):
using System.Xml.Linq;
using System.Xml.XPath;
XDocument finalDoc = null;
foreach (string file in Directory.GetFiles(#"PluginsFolder", "*.xml"))
{
if(finalDoc == null)
{
finalDoc = XDocument.Load(File.OpenRead(file));
}
else
{
XDocument xdocAdditional = XDocument.Load(File.OpenRead(file));
finalDoc.Root.XPathSelectElement("/doc/members")
.Add(xdocAdditional.Root.XPathSelectElement("/doc/members").Elements());
}
}
// Supply the navigator that rest of the XmlDocumentationProvider code looks for
_documentNavigator = finalDoc.CreateNavigator();
Kirans solution works very well. I ended up using his approach but by creating a copy of XmlDocumentationProvider, called MultiXmlDocumentationProvider, with an altered constructor:
public MultiXmlDocumentationProvider(string xmlDocFilesPath)
{
XDocument finalDoc = null;
foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
{
using (var fileStream = File.OpenRead(file))
{
if (finalDoc == null)
{
finalDoc = XDocument.Load(fileStream);
}
else
{
XDocument xdocAdditional = XDocument.Load(fileStream);
finalDoc.Root.XPathSelectElement("/doc/members")
.Add(xdocAdditional.Root.XPathSelectElement("/doc/members").Elements());
}
}
}
// Supply the navigator that rest of the XmlDocumentationProvider code looks for
_documentNavigator = finalDoc.CreateNavigator();
}
I register the new provider from HelpPageConfig.cs:
config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/")));
Creating a new class and leaving the original one unchanged may be more convenient when upgrading etc...
Rather than create a separate class along the lines of XmlMultiDocumentationProvider, I just added a constructor to the existing XmlDocumentationProvider. Instead of taking a folder name, this takes a list of strings so you can still specify exactly which files you want to include (if there are other xml files in the directory that the Documentation XML are in, it might get hairy). Here's my new constructor:
public XmlDocumentationProvider(IEnumerable<string> documentPaths)
{
if (documentPaths.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(documentPaths));
}
XDocument fullDocument = null;
foreach (var documentPath in documentPaths)
{
if (documentPath == null)
{
throw new ArgumentNullException(nameof(documentPath));
}
if (fullDocument == null)
{
using (var stream = File.OpenRead(documentPath))
{
fullDocument = XDocument.Load(stream);
}
}
else
{
using (var stream = File.OpenRead(documentPath))
{
var additionalDocument = XDocument.Load(stream);
fullDocument?.Root?.XPathSelectElement("/doc/members").Add(additionalDocument?.Root?.XPathSelectElement("/doc/members").Elements());
}
}
}
_documentNavigator = fullDocument?.CreateNavigator();
}
The HelpPageConfig.cs looks like this. (Yes, it can be fewer lines, but I don't have a line limit so I like splitting it up.)
var xmlPaths = new[]
{
HttpContext.Current.Server.MapPath("~/bin/Path.To.FirstNamespace.XML"),
HttpContext.Current.Server.MapPath("~/bin/Path.To.OtherNamespace.XML")
};
var documentationProvider = new XmlDocumentationProvider(xmlPaths);
config.SetDocumentationProvider(documentationProvider);
I agree with gurra777 that creating a new class is a safer upgrade path. I started with that solution but it involves a fair amount of copy/pasta, which could easily get out of date after a few package updates.
Instead, I am keeping a collection of XmlDocumentationProvider children. For each of the implementation methods, I'm calling into the children to grab the first non-empty result.
public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
{
private IList<XmlDocumentationProvider> _documentationProviders;
public MultiXmlDocumentationProvider(string xmlDocFilesPath)
{
_documentationProviders = new List<XmlDocumentationProvider>();
foreach (string file in Directory.GetFiles(xmlDocFilesPath, "*.xml"))
{
_documentationProviders.Add(new XmlDocumentationProvider(file));
}
}
public string GetDocumentation(System.Reflection.MemberInfo member)
{
return _documentationProviders
.Select(x => x.GetDocumentation(member))
.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x));
}
//and so on...
The HelpPageConfig registration is the same as in gurra777's answer,
config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/")));

Adobe Flex: memory leak when pushView an popView

I am having problems of memory leaks with an app I had build in Adobe Flex, using Flex Builder. After using it for 30-40 minutes it starts to go slower and slower.
The app shows images as a catalog, but when I push and pop the views my memory rise considerably.
My thoughts are that if I set all my objects to null and dispose all the bitmapdata that I use I could free enough memory to keep using the app with no problems, but it seems like the problem is not there.
I have 3 views in my app menuView.mxml,categoriesView.mxml and productsView.mxml.
My App starts up in my pc (not in tablet) with TotalMemory: 47Mb and Private Memory : 88 MB
After pushing and poping the views for 5 times I get TotalMemory: 61Mb and Private Memory : 101 MB
Imagine if I do this several times. The app begins to go very slow in my Ipad or my Samsung Galaxy Tab.
Why is this happening? I have no idea how to solve this.
Please help! Thanks a lot!!
I put some code below.
When I am in menuView I use the following code to push the view from menuView to categoriesView
protected function button3_clickHandler(event:MouseEvent):void
{
if((FlexGlobals.topLevelApplication.getIdClienteServidorCompraActual( )!=null)&& (FlexGlobals.topLevelApplication.getIdClienteServidorCompraA ctual()>0))
{
navigator.pushView(categoriesView);
}
}
When I am in categoriesView I use the following code to push the view from categoriesView to productsView. In this view I have 3 buttons for each category.
protected function buttonC1_clickHandler(event:MouseEvent):void
{
//Categoria general con todos
var ab:ArrayCollection = getIdAmbienteServidor();
cleanMemory();
navigator.pushView(productsView, null);
}
private function cleanMemory():void
{
result.splice(0);
result = null;
System.gc();
}
When I am in productsView I use the following code to pop(I USE PUSH INSTEAD OF POP DUE TO DIFFERENT OPTIONS I HAVE) the view from productsView to categoriesView .
protected function button1_clickHandler(event:MouseEvent):void
{
cleanMemory();
navigator.pushView(menuView);
}
private function cleanMemory():void
{
if(image1 != null)
{
image1.source = "";
if(image1.bitmapData != null)
{
image1.bitmapData.dispose();
}
}
if(image2 != null)
{
image2.source = "";
if(image2.bitmapData != null)
{
image2.bitmapData.dispose();
}
}
if(result != null)
{
result.splice(0);
result = null;
}
if(result1 != null)
{
result1.splice(0);
result1 = null;
}
if(result2 != null)
{
result2.splice(0);
result2 = null;
}
dbConnection = null;
object1 = null;
object2 = null;
dataToSave = null;
cGreyImageSmallAsset = null;
cRedImageSmallAsset.bitmapData.dispose();
cRedImageSmallAsset = null;
cOrangeImageAsset.bitmapData.dispose();
cOrangeImageAsset = null;
cGreenImageAsset.bitmapData.dispose();
cGreenImageAsset = null;
cPinkImageAsset.bitmapData.dispose();
cPinkImageAsset = null;
cBlueImageAsset.bitmapData.dispose();
cBlueImageAsset = null;
cGreyImageAsset.bitmapData.dispose();
cGreyImageAsset = null;
cRedImageAsset.bitmapData.dispose();
cRedImageAsset = null;
cGreenImageSmall = null;
cOrangeImageSmall = null;
cPinkImageSmall = null;
cBlueImageSmall = null;
cGreyImageSmall = null;
cRedImageSmall= null;
cGreenImage = null;
cPinkImage = null;
cBlueImage= null;
cGreyImage = null;
cRedImage= null;
cOrangeImage= null;
System.gc();
}
I load the images with.
private function setImages():void
{
if(object1!=null)
{
panelLeft.visible = true;
buttonLeftMore.visible = true;
image1.source = "file://" + File.applicationStorageDirectory.nativePath + "/b"+object1.idArchivo+"_500.jpg";
setObject1MainTexts();
}
else
{
image1.source = "";
panelLeft.visible = false;
buttonLeftMore.visible = false;
}
if(object2!=null)
{
panelRight.visible = true;
buttonRightMore.visible = true;
image2.source = "file://" + File.applicationStorageDirectory.nativePath + "/b"+object2.idArchivo+"_500.jpg";
setObject2MainTexts();
}
else
{
image2.source = "";
panelRight.visible = false;
buttonRightMore.visible = false;
}
}
When I am in categoriesView i use the following code to pop(I USE PUSH INSTEAD OF POP DUE TO DIFFERENT OPTIONS I HAVE) the view from categoriesView to menuView.
protected function button1_clickHandler(event:MouseEvent):void
{
cleanMemory();
navigator.pushView(menuPrincipalBelda);
}
private function cleanMemory():void
{
result.splice(0);
result = null;
System.gc();
}
Thanks in advance!
I discover the problem: I was not removing the EventListener, now is working fine.
I leave you guys with some examples. If you do not remove them, then memory increases a lot! Be careful with this.
stage.addEventListener("keyUp", handleButtons, false, 1);
stage.removeEventListener("keyUp",handleButtons,false);
xeasy.addEventListener(ResultEvent.RESULT, AppWS_resultHandler);
xeasy.removeEventListener(ResultEvent.RESULT,AppWS_resultHandler, false);
Thanks to everybody!!
Remember that Flex Builder uses the Flash virtual machine and this frees the memory when considered necessary, even if you try to force him to clean the garbage collector will not do if you have free memory and does not consider it necessary.
Test the movie embedded in the images you need in the application, and not constantly loading. Since this may be letting some low-level relationship.
regards
When I worked with GC I found that it became of advantage to try several different ways! I used 3 in total over time, but just can only find one at the moment as this was the one noticeable better working in this App. so here it is and if needed I can send the other two as well must just look for them!
private var gcCount:int;
private function startGCCycle():void {
gcCount = 0;
addEventListener(Event.ENTER_FRAME, doGC);
}
private function doGC(evt:Event):void {
flash.system.System.gc();
if(++gcCount > 1) {
removeEventListener(Event.ENTER_FRAME, doGC);
setTimeout(lastGC, 40);
}
}
private function lastGC():void{
flash.system.System.gc();
}
]]>
I just was looking at something else which you should try as well it worked for me very well!
FlexGlobals.topLevelApplication.deleteReferenceOnParentDocument(this) I used these in a creationComplete in MXML where I had to open 45 Main Canvases one after another but each time with three PopUps = 180 which was easy to handle in the way I worked the GC in! regards aktell

DRY up logic to handle insert/update/delete of many to many records in ASP.NET MVC 3

In my application, I have a fair number of many to many relationships. I find that I'm writing a fair bit of code that looks something like this (I have taken the example code from the Contoso sample project for ease of reference):
private void UpdateInstructorCourses(int[] selectedCourses, Instructor instructorToUpdate)
{
if (selectedCourses == null)
{
instructorToUpdate.Courses = new List<Course>();
return;
}
var selectedCoursesHS = new HashSet<int>(selectedCourses);
var instructorCourses = new HashSet<int>(instructorToUpdate.Courses.Select(c => c.CourseID));
foreach (var course in db.Courses)
{
if (selectedCoursesHS.Contains(course.CourseID))
{
if (!instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Add(course);
}
}
else
{
if (instructorCourses.Contains(course.CourseID))
{
instructorToUpdate.Courses.Remove(course);
}
}
}
}
My many to many relationships are, by and large, composed of a central table with a lot of data and a bunch of lookup tables that have Id and Name fields.
The above is great when there's one or two many-to-manys but it gets a bit tedious and I have multiple methods which all follow the same basic logic above, but obviously use different dbsets in the foreach and a different object as the second param.
Is there a nice way to DRY the multiple methods up?
You should be able to create a generic method along these lines (not tested, and not compiled against a real EF context)
private void UpdateCollection<T>(int[] selectedIds, ICollection<T> dbCollection, ICollection<T> collectionToUpdate, Func<T, int> idGetter)
{
if (selectedIds == null)
{
collectionToUpdate.Clear();
return;
}
var selectedIdsHS = new HashSet<int>(selectedIds);
var idsToUpdate = new HashSet<int>(collectionToUpdate.Select(idGetter));
foreach (var elem in dbCollection)
{
var id = idGetter(elem);
if (selectedIdsHS.Contains(id))
{
if (!idsToUpdate.Contains(id))
{
collectionToUpdate.Add(elem);
}
}
else
{
if (idsToUpdate.Contains(id))
{
collectionToUpdate.Remove(elem);
}
}
}
}
It can be invoked as follows:
UpdateCollection(new[] {1,2,3}, db.Courses, instructor.Courses, c => c.CourseID);

Resources