Hi Quicoli,
Thanks for your answer.
The GetLastUnusedFolio is not being called right now, so it shouldn't be a
problem for the moment. My problem is that I need to read the *folio range*
from the DB and update it before assignment to the Document, but I need to
"Lock" the transaction until it's finished, otherwise another
thread/request to the same resource will update the folio range before and
a Stale object exception will be thrown.
What i've tried is this:
- Locking the *entity* and *range* objects (in the GetNextFolioNumber) for
upgrade: session.Lock(entity, LockMode.Upgrade); and later
session.Lock(range, LockMode.Upgrade);, which throws a Stale Object
Exception.
- Using a lock on the method:
lock(locker) {
GetNextFolioNumber(...);
}
which results in a UNIQUE KEY violation.
- Both of the above: results in a Stale object exception.
I've implemented a session per request on the nancy bootstrapper, which
works fine for single, non-concurrent, requests. In this case, I need to
create N Documents with many consecutive requests (spanning different
threads).
In short, what I need to do is this, but with two, or more,
threads/requests:
- Read a folio range.
- Select the current folio
- Increment the current folio number
- Assign the current folio to the Document
- Save the changes.
I'm guessing I need to lock the row for the entire transaction, but that
would mean to modify the session.Transaction depending on the requested
resource.
Lastly, I forgot to mention, the project is a REST API.
There is something I'm missing regarding the Nancy Requests and how
Transactions are handled by the database, but haven't found that on my
research.
Thanks in advance for any hints :).
El domingo, 17 de abril de 2016, 9:46:39 (UTC-3), Quicoli escribió:
>
> Hi,
>
> I believe getlastunusedfolio should lock your table and your transction
> scope should be something like lock and wait.
>
> This way the first one in, lock the access and the subsequent requests
> keep waiting for the first one to finish...
>
> This is an idea...
> Em 17/04/2016 07:18, "Nicolás Mancilla" <[email protected]
> <javascript:>> escreveu:
>
>> Hello Everyone!
>>
>> I have a question regarding concurrent requests.
>>
>> I have an two entities:
>>
>> class Document {
>>
>> FolioNumber // Long/bigint
>> other_properties...
>>
>> }
>>
>> class FolioRange {
>>
>> CurrentFolio // Long/bigint
>> Start // The first folio in the range
>> End // The last folio in the range
>> IsUsed // When the CurrentFolio >= End
>> DateCreated
>> IsCurrentRange
>>
>> DocumentType
>> RangeStatus
>>
>> }
>>
>> (Imagine that these are valid POCOs please :)
>>
>> Every time a Document is inserted to the database, the
>> Document.FolioNumber is set to the FolioRange.CurrentFolio. The logic is
>> this:
>>
>> public long GetNextFolioNumber(string documentType, Tenant tenant)
>> {
>> // Check if there is a folio range defined
>> var entity = session.QueryOver<FolioRange>()
>> .Where(
>> i =>
>> i.Tenant.Id == tenant.Id && i.DocumentType ==
>> documentType && i.IsCurrentRange &&
>> i.Status == RangeStatus.Enabled)
>> .Take(1)
>> .SingleOrDefault();
>> if (entity == null)
>> {
>> return 0;
>> }
>>
>> // Retrieve an unused folio, if there are any
>> var folio = GetLastUnusedFolio(entity);
>> if (folio > 0)
>> {
>> return folio;
>> }
>>
>> FolioRange range;
>>
>> // If the folio range has no folios left,
>> // try to change to the newest folio range
>> if (entity.IsUsed)
>> {
>> log.InfoFormat(
>> "The folio range '{0}' has been consumed. Trying to
>> switch to the next one (if there is one).",
>> entity.Id);
>> // Update the old folio range to disable it
>> entity.IsCurrentRange = false;
>> entity.Status = RangeStatus.Disabled;
>> session.Update(entity);
>> range = session.QueryOver<FolioRange>()
>> .Where(
>> i =>
>> i.Tenant.Id == tenant.Id && i.DocumentType ==
>> documentType &&
>> i.CreatedDate > entity.CreatedDate)
>> .Take(1)
>> .SingleOrDefault();
>>
>> // If there is no newest folio range, return
>> if (range == null)
>> {
>> return 0;
>> }
>>
>> log.InfoFormat("Successfully switched to folio range
>> '{0}'.", entity.Id);
>> // If there was a newest folio range, update it.
>> range.IsCurrentRange = true;
>> range.Status = RangeStatus.Enabled;
>> }
>> else
>> {
>> log.InfoFormat("Using folio range '{0}'; Current folio:
>> {1}", entity.Id, entity.CurrentFolio);
>> range = entity;
>> }
>>
>> // Set the current folio as result and increment the current
>> folio.
>> folio = range.CurrentFolio;
>> range.CurrentFolio++;
>> session.Update(range);
>> return folio;
>> }
>>
>>
>> This works well when there is only 1 request, but when there are more than
>> 1, then there is a race condition when the range.CurrentFolio++ is not
>> updated and two Documents get created with the same folio number, which is
>> not allowed.
>>
>>
>> I've tried using session.Lock(range, LockMode.Upgrade), without luck (a
>> UNIQUE KEY Violation is triggered). The Session.Transaction isolation level
>> is ReadCommited.
>>
>>
>> I'm using NHibernate 4.0.4 and Nancy 1.4.3.
>>
>>
>> Any suggestions, book references or any reading will be greatly appreciated.
>>
>>
>> Thanks!
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "nhusers" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected] <javascript:>.
>> To post to this group, send email to [email protected]
>> <javascript:>.
>> Visit this group at https://groups.google.com/group/nhusers.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
--
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/nhusers.
For more options, visit https://groups.google.com/d/optout.