We solved a similar issue in our system using distributed locking (Hazelcast).
In Application.
Hope this helps.
Hello there,
I've got a WO app, running with FrontBase, pretty standard setup.
The one slightly non-standard thing is that for some specific EOs, I generate fixed PKs through
ec.insertObjectWithGlobalID(myeo,EOKeyGlobalID.globalIDWithEntityName(entityName,new Object[]{primaryKey}))
and later I fetch them with those fixed PKs.
Till my app run single-instance, all worked well. As soon as I went multi-instance, I'm starting getting locking problems FrontBase-level. That sort of makes sense -- after all it's well possible that different instances access the DB at the same moment
-- but for sweet world, I can't find a good solution.
(i) My first attempt was to set pessimistic locking FrontBase-level -- the idea was that the other instance simply would be forced by the DB server to wait a moment whilst the first one does its work. This failed due to
- the fact that FrontBase locks SELECTS in pessimistic mode, too, which probably made all the instances run pretty slow (I am not entirely sure it's the culprit, but I checked that FrontBase indeed does lock SELECTS in pessimistic mode, and I _do_ know
my app run much slower in this setup, so it seems a good guess).
- anyway, I kept getting deadlocks: I did implement databaseContextWillOrderAdaptorOperations delegate method to order operations by entity to prevent table-based deadlocks, *BUT* I am using er.jgroups.ERJGroupsSynchronizer to sync changes betw. instances,
and it does not seem it can be told to order operations the same way, and thus I got deadlocks, when Synchronizer saved tables in order B, A ... and my own code from another instance at the same moment updated in order A, B, ...
(ii) so I've switched to optimistic locking FrontBase-level ("jdbc:FrontBase://... .../isolation=read_committed/locking=optimistic"). That, though, brought other problems I have problems to cope with:
(a) concurrent deletions
It might happen two instances try to delete an EO at the same moment. If this happens, I am getting an exception that object cannot be found (not surprisingly :)). At the moment, the best solution I have found so far is
===
static void _saveTolerantlyChangesInEC(EOEditingContext ec) { // essentially same logic as Wonder's saveTolerantly
try { // solve the concurrent deletion problem
ec.saveChanges()
} catch (EOGeneralAdaptorException exception) {
NSDictionary eui=exception.userInfo()
EOAdaptorOperation adaptorOp=eui[EOAdaptorChannel.FailedAdaptorOperationKey]
EODatabaseOperation databaseOp=eui[EODatabaseContext.FailedDatabaseOperationKey]
NSDictionary snapshot = databaseOp.dbSnapshot()
if (adaptorOp.adaptorOperator()==EODatabaseOperation.AdaptorDeleteOperator) {
EOEnterpriseObject delobj=ec.deletedObjects().find { it.primaryKeyNumericValue == snapshot['uid'] } // 'uid' is PK
if (delobj) ec.forgetObject(delobj)
_saveTolerantlyChangesInEC(ec) // OK, we have gotten rid of the offending object, let's try again
} else throw exception;
}
}
===
Seems rather on the convoluted side -- can it be done in an easier way? -- but whatever else I did, it did not work (e.g., without forgetting the object, a retry tried to delete again, triggering again the same exception.
Seems to work all right, nevertheless. Still, if there's a simpler solution, I'd like to see it.
(b) concurrent inserts
This problem is caused by my using fixed PKs (though if I did not, I might be getting duplicated EOs instead). If two instances try to insert a new EO with the same PK, I'm (naturally) getting a constraint PK violation exception from the database.
And I can't find a way to solve it :(
I can (and do) try to fetch the offending object -- and since the other instance committed meantime, I get it all right:
===
... code as above ...
if (ocs_is_primary_key_constraint(exception) && adaptorOp.adaptorOperator()==EODatabaseOperation.AdaptorInsertOperator) {
NSArray pka=entity.primaryKeyAttributes()
EOQualifier pkq=ERXEOAccessUtilities.qualifierFromAttributes(pka,adaptorOp.changedValues()) // contains the PK
EOFetchSpecification fs=new EOFetchSpecification(entityName,pkq,null)
fs.setRefreshesRefetchedObjects(YES)
NSArray objs=ec.objectsWithFetchSpecification(fs)
if (objs.count()==1) {
// OK, my code does get here, the object inserted by other instance is fetched all right. But what now?!?
???
_saveTolerantlyChangesInEC(ec) // let's try again
... ...
===
What to do at the ??? place? If I do nothing, the insert operation stays in EC and fails again.
I've tried to remove the newly added object from ec.insertedObjects. Strangely enough it did work (i.e., it has been removed -- self evidently, internally insertedObjects is a mutable array), but did not help -- this way led to the dreaded "rowDiffsForAttributes:
snapshot in com.webobjects.eoaccess.EODatabaseOperation {_dbSnapshot = {}; ..." exceptions.
Currently I am recording the PK and entity, and in my databaseContextWillOrderAdaptorOperations I do not add adaptor operation for such an insert. This seems to sort of work, but is TERRIBLY convoluted -- there must be a better solution?
(c) concurrent updates
When I get the "condition 379. Optimistic locking: multiple transaction conflict detected" I do essentially nothing -- I just wait a random couple of tenths of second, and retry.
Again, is there as better solution? I keep pretty often the same exception again; perhaps I should wait for a longer random time? But that might slow the application perceptibly (well, the exceptions do, definitely).
Anyway, seems to me these problems should have been encountered thousand times (essentially always with multi-instance setup) and solved in a clean and easy way long long ago -- but perhaps I'm just dumb and blind, or my google-fu is terribly lacking,
but whatever I do, I can't find a good solution?
Will be really grateful for any advice,
OC
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Help/Unsubscribe/Update your Subscription: