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.
Webobjects-dev mailing list ([email protected])
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com
This email sent to [email protected]