This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Hi Guys, I have a multi-threaded application which uses a Lightspeed-based repository (using a repository derived from RepositoryBase) and everything works fine, but I recently noticed something weird. When the system is running and I update something manually in the database, it is not picked up by the repository (ie. the system keeps using the previous value). I thought I had a problem with caching, but it seems to have nothing to do with caching, but with the way the PerThreadUnitOfWorkScope works. I initiate the system using the following bit of code: var repository = new MyRepository(new PerRequestUnitOfWorkScope<MyUnitOfWork>(context));(where MyUnitOfWork is the UnitOfWork created by the designer and MyRepository is the repository I created, deriving from RepositoryBase<MyUnitOfWork> I then, in one of the methods of the repository (a simple Get method, returning all rows from one of the tables in the model) added some logging for writing the HashCode of the Unit Of Work (UnitOfWorkScope.Current.GetHashCode()). I then ran my application (the console version). This application spawns a new thread using ThreadPool.QueueUserWorkItem (ie, using worker threads, so it's not REALLY spawning new threads) and then calls the repository method on this thread. I noted the thread and the hash code. I then re-ran this function, so it called ThreadPool.QueueUserWorkItem again. The log showed I indeed had another thread, but the HashCode of the Unit Of Work was the same. I actually expected a different hash code (as I specified PerThreadUnitOfWorkScope)... My application is too elaborate to send to you guys (not to mention IP issues), but I hope that you understand my problem from my description and can point me to what I've done wrong. If not, I'll see if I can build a sample app to show my problem. Kind regards, Robert |
|
|
Hmmm, just realized that my test may be flawed, depending on how you implemented GetHashCode(), but I didn't see any other field on UnitOfWork that could id it uniquely... Anyway, my question remains... ;-) |
|
|
Hi Robert, Thanks for reporting this issue. It looks like we had a bug in PerThreadUnitOfWorkScope which caused different scopes to share the same UOW in certain situations. This is now fixed and the fix will be in nightly builds dated 21 Nov 2008 and above. Please let us know if you still see problems. Note that since (as you observe) ThreadPool reuses threads, a unit of work may still end up being shared across multiple work items -- if two items end up running on the same thread pool thread then they will acquire the same unit of work. If this is an issue for you then one workaround may be to perform a SaveChanges(true) at the end of each work item; this clears the UOW's identity map, which will cause it to reload stuff from the database next time it is queried. |
|
|
Hi Ivan, A quick test shows that your fix worked (at least for me ;-)). Thanks a lot. Quick turnaround as always. I don't think your note affects me, but I will keep an eye on it, to make sure we are not affected by this behaviour. Thanks, Robert :-) |
|
|
Hi Ivan, I just actually DID see the problem you described where sometimes a thread picks up any changes I make to the database and sometimes it doesn't. The problem is that (besides the fact that our source control is offline, so I'm not allowed to make any changes to this source) I'm using a repository based on RepositoryBase<TUnitOfWork>, which only have SaveChanges() without the reset parameter. I guess I could add a method to my repository, call the appropriate SaveChanges on the Current UnitOfWork, but I thought I'd ask you guys first to see if it would be a good idea to have a SaveChanges(bool reset) on RepositoryBase<UnitOfWork> and IRepository as well? Or is there a better solution? Thanks, Robert |
|
|
A SaveChanges(bool reset) method on IRepository kinda feels encapsulation-breaking, in that the repository should be an abstract representation of the data store that isn't too bound up with the mechanics of the unit of work and the identity map. So I think we would be reluctant to add that at the IRepository/RepositoryBase level. This isn't to suggest it would be wrong for you to add such a method at the level of your repository, because it is only pragmatic to let your repository API reflect the specific requirements of your program. It sounds like the "better" solution would be to create a custom scope class which understands the thread pool and creates a unit of work per work item, rather than a unit of work per physical thread. I'm not sure how to do this though, without creating a design where you had to go through a custom API (maybe on the repository) to queue the work item -- I can't otherwise find a way for a scope to know when it would need to create or dispose a unit of work. Still possibly something to consider. |
|
|
Hi Ivan, Of course you are right about the encapsulation... But from the remainder of your explanation, it looks like I might be mis-interpreting your earlier explanation or I'm experiencing another issue. Let me try to explain what I'm doing and what is happening: I have a windows service that on start, loads a controller class. Then the service loads one or more objects (Schedules) which are handed to the controller class, which stores them internally. These objects (of which I, for my test, have only one) periodically fire events, which are handled by the controller class. The event handler retrieves Job records from the database (using the repository) and then, for each of the jobs, spawns a thread from the threadpool to actually 'run' the job. (so now we have a minimum of two threads. Not sure, from the top of my head, if the event runs on a separate thread as well, but I suspect it does). Then each job, running on a separate thread (from the threadpool) retrieves additional information (devices) and for each of these devices spawns another thread from the threadpool to do some actual work. A device has a list of actions it can perform. In my test setup, I have 1 schedule, 1 job and 1 device with 1 action. With this setup, I now have (not taking the event into account) one main thread, one workerthread for the job and 1 workerthread for the device. I run the test setup, trigger the schedule to fire an event and everything runs fine. With the windows service (and the whole test setup) still running, I now change the action attached to the device, in the database and trigger the schedule again. What I would expect, as the device ran its action on a separate thread, so it had its own unit of work, which ended with the thread's completion, is that the second time the test is run, the new action is picked up. When I actually ran my test, this is what happened (with the latest nightly build installed). However, the second time I did this (changing the action and re-triggering the schedule), the previous action (from before the second change) was run, indicating to me that it kept the unit of work. Since I'm running limited threads in this setup and both worker threads end when the device is done running the action, I suspect the same thread is recycled from the Threadpool. However, I expected that the thread would have its state cleared and would therefore get a new Unit Of Work when it was re-used. Looks like this last assumption might be wrong and I would have to, somehow, clear the workerthread's state before doing anything or roll my own threadpool (brrrrrrrrr....) I hope this makes things a bit clearer and I hope you can help me out here, so we can figure out if this is just me, a Lightspeed thing (issue or a by-design feature) or a combination. Note that I may have oversimplified things a bit, so don't hammer me about design issues, etc ;-) Thanks in advance, Robert |
|
|
The LightSpeed PerThreadUnitOfWorkScope is naive. It stores a UnitOfWork in per-thread storage, and whenever it is asked for its current unit of work, it returns that. So if (when!) the ThreadPool recycles a thread, that thread will still have the same unit of work associated with it, because the ThreadPool does not (as far as I know) clear out thread-local storage -- it does not "have its state cleared." From the MSDN documentation on the ThreadPool class (emphasis added): "When the thread pool reuses a thread, it does not clear the data in thread local storage or in fields that are marked with the ThreadStaticAttribute attribute. Therefore, data that is placed in thread local storage by one method can be exposed to any other method that is executed by the same thread pool thread." So this is outside our control. PerThreadUnitOfWorkScope has no way of knowing whether a request for the current unit of work on a thread is being made by the same ThreadPool work item or a new one. All it can do is go "yep, you're from the same thread, have the same unit of work." A hypothetical PerWorkItemUnitOfWorkScope would get around this, but would need some way of associating units of work with ThreadPool work items, rather than naively with the physical threads. The only way I can think of to do this is to wrap access to the ThreadPool, so that the wrapper could inject the new UOW via the argument to the WaitCallback, but I am not sure how to do this in a nicely encapsulated way. On the other hand I still think that would be better than rolling your own thread pool...! |
|
|
Hi Ivan, Makes sense. I've also read up on Thread Local Storage and could indeed find no easy way to clear this data (specifically in ThreadPool threads). So I guess that leaves me with a few options: - Switch to regular Threads. While this would work, I would not have the advantage of having the system take care of much of the complexities and keeping a cap on the number of running threads. While I now only have limited number of threads, the system is designed to handle thousands of concurrent requests. Having thousands of threads running would not be good for performance, so I would again have to resort to manually managing the number of simultaneous threads. - Work out your idea of having a PerWorkItemUnitOfWorkScope. Interesting concept, but I'm afraid I won't have the time to get it done... Will think about it though... - Implement the option we discussed earlier (have a SaveChanges(bool reset) or override the BaseRepository's SaveChanges to perform some additional work) on the repository. Not the nicest solution, but relatively simple to implement. I could do a SaveChanges(true) or have a Clean() or Refresh() method to clean up the 'old' Unit Of Work at the beginning of a 'new' thread. - Live with the situation. This wouldn't be TOO bad, at least for now, but probably not the best solution for the long run, especially when we're adding other applications to the system, which will make use of the same database... I'll have to think about. Thanks for your input! Robert :) |
|
|
We're also seeing this problem.
Since this is an old post, I wanted to see if there was any new info on using ThreadPool threads with Lightspeed.
|
|
|
No, there's no further info I'm afraid (unless Robert has an outcome that he can share). I assume the problem you're referring to is that the PerThreadUnitOfWorkScope really is per-thread not per-work item and therefore the same UOW can be handed out to two different work items? If so, again the recommendations would be to either manage the UOWs explicitly rather than using a scope (which may be tricky if UOW construction and disposal is encapsulated within a Repository object) or to implement something that manages UOW lifetimes on a per-work item basis, e.g. by wrapping the WaitCallback. |
|
|
Not too much further info from me either. What I've done, which seems to work well is create a property Repository which references a ThreadStatic field _repository (of my repository type, which derives from RepositoryBase). In each thread, I call the property. In the property, I check if the _repository field is NULL (which it is for a new thread) and if it is, create a new Repository. This seems to work well, but you need to be careful in the main thread though, as this one is never released, so any entities loaded will be kept/cached, so stale data can occur. With this setup, I might be able to return to using the ThreadPool again, but I haven't had time to get around to trying this yet. Still still is not the perfect solution, but it seems to do the job for now ;-) Robert |
|