FrontBase triggers howto (was: Never save objects which don't pass a test (was: searching for a weird deletion))

2015-02-21 Thread OC
Hello there,

after having played for a time with constraints, I have decided to use a 
trigger instead -- main reason is that I really want the validation performed 
on INSERT only; a constraint might get evaluated in other occassions, too.

Nevertheless, I can't find a decent way of doing that in FrontBase. Is here 
anybody who knows its dialect well enough?

Ideally, the trigger would look more or less like this:

===
create trigger TEST_PO_TRIGGER before insert on T_PRICE_OFFER
referencing new po for each row when (... my complex validation condition ...)
raise exception 'Price offer not valid'
===

presumed RAISE EXCEPTION worked in FrontBase, which it does not, nor I was able 
to find any other statement/function to rollback the transaction and report an 
error.

Presumed there's no such function at all, the first fallback would be

===
create trigger TEST_PO_TRIGGER before insert on T_PRICE_OFFER
referencing new po for each row when (... my complex validation condition ...)
set po.C_UID = null
===

which would exploit the fact C_UID (which happens to be the PK) can't be null. 
Alas, again, whatever I tried, I haven't been able to find any SET syntax which 
would work :(

Eventually, the only trigger I found working was

===
create trigger TEST_PO_TRIGGER after insert on T_PRICE_OFFER
referencing new po for each row when (... my complex validation condition ...)
update T_PRICE_OFFER set C_UID = null where C_UID=po.C_UID
===

but darn, that seems to be a _terribly_ convoluted work-around for the desired 
behaviour!

As always, I'll be grateful for any advice,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-21 Thread OC
Chuck,

On 20. 2. 2015, at 19:37, Chuck Hill ch...@gevityinc.com wrote:

 One way would be to twist the DB to do the complete restraint to me, 
 something like to a pseudo-code “inserted_row.attr=MAX(SELECT attr FROM 
 this_table WHERE another_attr.isValid) AND couple more similar conditions” 
 -- frankly I am not sure whether the database (FrontBase) can do that at 
 all, and if it can, definitely I don't know how to.
 
 I think you can do it as a Check constraint.  It needs to be a boolean 
 expression, but I am not sure it can include selects.  A quick experiment 
 would show if it can.

It looks like FrontBase allows me to use SELECT in check constraints, but I've 
bumped into another problem -- I need also to join to access values from 
another table, and I don't know how to express „column from the row just being 
inserted, not from any other table in the SELECT“?

My constraint expression (considerably simplified for readability) looks like 
this:

(select max(po.C_PRICE) from T_PRICE_OFFER po, T_AUCTION auc where 
po.C_AUCTION_ID=auc.C_UID and po.C_PRICE=auc.C_MAX_PRICE and 
po.C_CREATION_DATEC_CREATION_DATE)C_PRICE

to express a condition “PRICE of the inserted PRICE_OFFER must be higher than 
all PRICEs of all older PRICE_OFFERs belonging to the same auction, whose PRICE 
does not exceed the auction's MAX_PRICE”.

The problem is with the “older PRICE_OFFERs” part (“WHERE ... 
po.C_CREATION_DATEC_CREATION_DATE”) -- FrontBase complains that 
C_CREATION_DATE is ambiguous. Well it is -- all the tables in question contain 
such a column -- but how should I prefix it to tell the server „this is the 
CREATION_DATE of the row which is being inserted“?

Thanks a lot,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-21 Thread OC
Anyway, back to the original question --

On 20. 2. 2015, at 18:27, OC o...@ocs.cz wrote:
 Actually _this_ should not be weird, this should be quite a normal code; 
 the only requirement is that check-and-save, i.e., conceptually,
 
 ===
 if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
  def 
 new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
  new.someAttribute=newAttributeValue
  eo.editingContext().saveChanges()
 }
 ===
 
 so that I can be absolutely sure that nobody stores an attribute value 
 which -- at the moment of COMMITTing the appropriate INSERT -- would not 
 pass the TEST

 I don’t think you can make a rock-solid guarantee from the app code level.
 
 Can't I? That's bad.

-- I have succeeded to consult with my client, and the option of „allowing to 
save any bid, determine whether it was valid or not in future“ is out.

On the other hand, he again suggests pessimistic locking: „why don't we simply 
lock the auction row when user reads the data to determine whether his bid is 
valid, and unlock when he saves the valid bid or when he determines the bid is 
not valid“?

I can see only one slight drawback -- unnecessary locks in case the bid 
validation fails -- but that should be a negligible problem, most bid attempts 
are valid (and _if_ they become invalid, then since other bid was entered 
shortly before, which would lead to optimistic exception anyway).

Far as I understand though based on your

===
On 24. 1. 2015, at 0:12, Chuck Hill ch...@gevityinc.com wrote:
 I doubt that lockObject() code in EOF has been run in… maybe forever.  It is 
 highly possible that it is causing EOF to get confused and resulting in the 
 errors below.  Get rid of the lockObject() calls and see if the problem below 
 goes away.
===

I guess the proper answer is „Well we can't use pessimistic row locking at all, 
since EOF does not support it reliably, and that's that.“

Is that right?

Thanks again,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-21 Thread Chuck Hill
I think it is a fair expectation that EOF may not properly support optimistic 
locking.  I am not even sure how to set it up properly.  There are some methods 
on EOEditingContext and EODatabaseContext, and one the EODatabaseContext 
Delegate.

Chuck

On 2015-02-21, 3:42 AM, OC wrote:

Anyway, back to the original question --

On 20. 2. 2015, at 18:27, OC o...@ocs.czmailto:o...@ocs.cz wrote:
Actually _this_ should not be weird, this should be quite a normal code; the 
only requirement is that check-and-save, i.e., conceptually,
===
if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
  def 
new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
  new.someAttribute=newAttributeValue
  eo.editingContext().saveChanges()
}
===
so that I can be absolutely sure that nobody stores an attribute value which -- 
at the moment of COMMITTing the appropriate INSERT -- would not pass the TEST

I don’t think you can make a rock-solid guarantee from the app code level.
Can't I? That's bad.

-- I have succeeded to consult with my client, and the option of „allowing to 
save any bid, determine whether it was valid or not in future“ is out.

On the other hand, he again suggests pessimistic locking: „why don't we simply 
lock the auction row when user reads the data to determine whether his bid is 
valid, and unlock when he saves the valid bid or when he determines the bid is 
not valid“?

I can see only one slight drawback -- unnecessary locks in case the bid 
validation fails -- but that should be a negligible problem, most bid attempts 
are valid (and _if_ they become invalid, then since other bid was entered 
shortly before, which would lead to optimistic exception anyway).

Far as I understand though based on your

===
On 24. 1. 2015, at 0:12, Chuck Hill 
ch...@gevityinc.commailto:ch...@gevityinc.com wrote:
I doubt that lockObject() code in EOF has been run in… maybe forever.  It is 
highly possible that it is causing EOF to get confused and resulting in the 
errors below.  Get rid of the lockObject() calls and see if the problem below 
goes away.
===

I guess the proper answer is „Well we can't use pessimistic row locking at all, 
since EOF does not support it reliably, and that's that.“

Is that right?

Thanks again,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-21 Thread Chuck Hill
I don’t know, I suggest asking on the FrontBase developers list.

On 2015-02-21, 2:55 AM, OC wrote:

Chuck,

On 20. 2. 2015, at 19:37, Chuck Hill 
ch...@gevityinc.commailto:ch...@gevityinc.com wrote:

One way would be to twist the DB to do the complete restraint to me, something 
like to a pseudo-code “inserted_row.attr=MAX(SELECT attr FROM this_table WHERE 
another_attr.isValid) AND couple more similar conditions” -- frankly I am not 
sure whether the database (FrontBase) can do that at all, and if it can, 
definitely I don't know how to.
I think you can do it as a Check constraint.  It needs to be a boolean 
expression, but I am not sure it can include selects.  A quick experiment would 
show if it can.

It looks like FrontBase allows me to use SELECT in check constraints, but I've 
bumped into another problem -- I need also to join to access values from 
another table, and I don't know how to express „column from the row just being 
inserted, not from any other table in the SELECT“?

My constraint expression (considerably simplified for readability) looks like 
this:

(select max(po.C_PRICE) from T_PRICE_OFFER po, T_AUCTION auc where 
po.C_AUCTION_ID=auc.C_UID and po.C_PRICE=auc.C_MAX_PRICE and 
po.C_CREATION_DATEC_CREATION_DATE)C_PRICE

to express a condition “PRICE of the inserted PRICE_OFFER must be higher than 
all PRICEs of all older PRICE_OFFERs belonging to the same auction, whose PRICE 
does not exceed the auction's MAX_PRICE”.

The problem is with the “older PRICE_OFFERs” part (“WHERE ... 
po.C_CREATION_DATEC_CREATION_DATE”) -- FrontBase complains that 
C_CREATION_DATE is ambiguous. Well it is -- all the tables in question contain 
such a column -- but how should I prefix it to tell the server „this is the 
CREATION_DATE of the row which is being inserted“?

Thanks a lot,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-21 Thread OC
Chuck,

On 21. 2. 2015, at 20:09, Chuck Hill ch...@gevityinc.com wrote:
 I think it is a fair expectation that EOF may not properly support optimistic

pessimistic, I guess?

 locking.  I am not even sure how to set it up properly.  There are some 
 methods on EOEditingContext and EODatabaseContext, and one the 
 EODatabaseContext Delegate.

Thanks!

One closely related question -- I suppose there is no way to explicitly set up 
the isolation level and locking discipline for a particular transaction 
(differently from the default of the JDBC connexion URL)? Or is there one?

Thanks again and all the best,
OC

 On 2015-02-21, 3:42 AM, OC wrote:
 
 Anyway, back to the original question --
 
 On 20. 2. 2015, at 18:27, OC o...@ocs.cz wrote:
 Actually _this_ should not be weird, this should be quite a normal code; the 
 only requirement is that check-and-save, i.e., conceptually,
 ===
 if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
   def 
 new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
   new.someAttribute=newAttributeValue
   eo.editingContext().saveChanges()
 }
 ===
 so that I can be absolutely sure that nobody stores an attribute value which 
 -- at the moment of COMMITTing the appropriate INSERT -- would not pass the 
 TEST
 
 I don’t think you can make a rock-solid guarantee from the app code level.
 Can't I? That's bad.
 
 -- I have succeeded to consult with my client, and the option of „allowing to 
 save any bid, determine whether it was valid or not in future“ is out.
 
 On the other hand, he again suggests pessimistic locking: „why don't we 
 simply lock the auction row when user reads the data to determine whether his 
 bid is valid, and unlock when he saves the valid bid or when he determines 
 the bid is not valid“?
 
 I can see only one slight drawback -- unnecessary locks in case the bid 
 validation fails -- but that should be a negligible problem, most bid 
 attempts are valid (and _if_ they become invalid, then since other bid was 
 entered shortly before, which would lead to optimistic exception anyway).
 
 Far as I understand though based on your
 
 ===
 On 24. 1. 2015, at 0:12, Chuck Hill ch...@gevityinc.com wrote:
 I doubt that lockObject() code in EOF has been run in… maybe forever.  It is 
 highly possible that it is causing EOF to get confused and resulting in the 
 errors below.  Get rid of the lockObject() calls and see if the problem below 
 goes away.
 ===
 
 I guess the proper answer is „Well we can't use pessimistic row locking at 
 all, since EOF does not support it reliably, and that's that.“
 
 Is that right?
 
 Thanks again,
 OC
 
 


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-21 Thread Chuck Hill


On 2015-02-21, 11:17 AM, OC wrote:

Chuck,

On 21. 2. 2015, at 20:09, Chuck Hill 
ch...@gevityinc.commailto:ch...@gevityinc.com wrote:
I think it is a fair expectation that EOF may not properly support optimistic

pessimistic, I guess?

Cough.  Yes.



locking.  I am not even sure how to set it up properly.  There are some methods 
on EOEditingContext and EODatabaseContext, and one the EODatabaseContext 
Delegate.

Thanks!

One closely related question -- I suppose there is no way to explicitly set up 
the isolation level and locking discipline for a particular transaction 
(differently from the default of the JDBC connexion URL)? Or is there one?

You could set up a second EOF stack and set the JDBC url isolation level and 
locking discipline to what you want and use that stack for these transactions.

Chuck


Thanks again and all the best,
OC

On 2015-02-21, 3:42 AM, OC wrote:
Anyway, back to the original question --
On 20. 2. 2015, at 18:27, OC o...@ocs.czmailto:o...@ocs.cz wrote:
Actually _this_ should not be weird, this should be quite a normal code; the 
only requirement is that check-and-save, i.e., conceptually,
===
if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
   def 
new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
   new.someAttribute=newAttributeValue
   eo.editingContext().saveChanges()
}
===
so that I can be absolutely sure that nobody stores an attribute value which -- 
at the moment of COMMITTing the appropriate INSERT -- would not pass the TEST
I don’t think you can make a rock-solid guarantee from the app code level.
Can't I? That's bad.
-- I have succeeded to consult with my client, and the option of „allowing to 
save any bid, determine whether it was valid or not in future“ is out.
On the other hand, he again suggests pessimistic locking: „why don't we simply 
lock the auction row when user reads the data to determine whether his bid is 
valid, and unlock when he saves the valid bid or when he determines the bid is 
not valid“?
I can see only one slight drawback -- unnecessary locks in case the bid 
validation fails -- but that should be a negligible problem, most bid attempts 
are valid (and _if_ they become invalid, then since other bid was entered 
shortly before, which would lead to optimistic exception anyway).
Far as I understand though based on your
===
On 24. 1. 2015, at 0:12, Chuck Hill 
ch...@gevityinc.commailto:ch...@gevityinc.com wrote:
I doubt that lockObject() code in EOF has been run in… maybe forever.  It is 
highly possible that it is causing EOF to get confused and resulting in the 
errors below.  Get rid of the lockObject() calls and see if the problem below 
goes away.
===
I guess the proper answer is „Well we can't use pessimistic row locking at all, 
since EOF does not support it reliably, and that's that.“
Is that right?
Thanks again,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-20 Thread Ramsey Gurley

On Feb 20, 2015, at 12:09 PM, OC o...@ocs.cz wrote:

 it must pass a slightly more complex test than uniqueness:
 
 - when inserted into PRICE_OFFERS table,
 - the newly inserted object must have value in column PRICE, which is higher,
 - than any (max) of the already existing objects in that table, which
 - have YES in a boolean column VALID, and
 - at the same moment, have same value in AUCTION_ID column as the inserted 
 object.
 
 (In fact the real condition is even more complex, but this is the gist of it: 
 consider an auction system, where a new bid added to a particular auction 
 must be higher than all previous valid bids for the same auction.)
 
 Nevertheless, I believe that when we are pursuing the 
 implement-the-behaviour-at-the-application-level way (unlike the check 
 restraint at the DB level), the particular TEST is actually irrelevant. The 
 gist is that it must not be possible to store an object which does not pass 
 TEST -- whatever the TEST tests.

Why is this a requirement? If highest price wins, then you only need to select 
the max price row where price offer date is less than auction end. If a few 
offers get thrown in there out of order, how does that break anything?

 
 See please again the [1] above -- the code must make sure that
 
 (a) when TESTing, the participating objects are a proper snapshot of database 
 contents of some moment in the past
 (b) when saving, the code must make sure that if the values of the snapshot 
 did change, the saving won't happen
 
 That should be sufficient, should it not?
 
 Does it make sense?
 
 Thanks a lot,
 OC
 


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-20 Thread OC
Chuck,

 Actually _this_ should not be weird, this should be quite a normal code; the 
 only requirement is that check-and-save, i.e., conceptually,
 
 ===
 if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
   def 
 new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
   new.someAttribute=newAttributeValue
   eo.editingContext().saveChanges()
 }
 ===
 
 so that I can be absolutely sure that nobody stores an attribute value which 
 -- at the moment of COMMITTing the appropriate INSERT -- would not pass the 
 TEST

Alas, since the TEST is (slightly) more complex than A!=B, I can't use a UNIQUE 
db restraint.

One way would be to twist the DB to do the complete restraint to me, something 
like to a pseudo-code “inserted_row.attr=MAX(SELECT attr FROM this_table WHERE 
another_attr.isValid) AND couple more similar conditions” -- frankly I am not 
sure whether the database (FrontBase) can do that at all, and if it can, 
definitely I don't know how to.

Nevertheless this is an interesting idea which I am going to pursue (if anybody 
happens to know the solution, either how to, or that it is not possible at all, 
of course I'll be grateful for an advice, before I dive into that).

Another way, the one I've tried to exploit so far, was implement the behaviour 
app-side:

 I believe that is the only way to absolutely ensure this.  I don’t think you 
 can make a rock-solid guarantee from the app code level.

Can't I? That's bad.

So far, I thought this very simple concept should be rock-solid, but probably I 
am overlooking something of importance, as so often:

=== eo's entity locks on the someRelationship FK (among others) ===
OSC.lock() // possibly superfluous; simplifies situation by serializing 
intra-instance
try {
  ec.unlock(); ec.lock() //* to make sure we get changes from other ECs now, by 
your excellent advice
  def rel=eo.someRelationship() // in DB there might be a newer value (saved 
meantime by another instance)...
  def attr=rel.someAttribute()  // ... but it is not possible that in DB is an 
_older_ value than this
  if (TEST(attr,newAttributeValue)) {
def 
new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
new.setSomeAttribute(newAttributeValue) // once set, I NEVER change this 
value
eo.addObjectToBothSidesOfRelationshipWithKey(new,'someRelationship')
eo.editingContext().saveChanges() // catching optimistic exceptions and 
repeating the process if they happen
  }
} finally {
  OSC.unlock()
}
===

My reasoning is that
- intra-instance, consistency is ensured by locked (single) OSC and by //* -- I 
am sure that I see the latest eo.someRelationship and its rel.someAttribute 
before saving, and thus the TEST is reliable; my own instance, even with 
concurrent requests, can't do anything wrong
- inter-instance, the optimistic locking based on someRelationship FK should 
prevent saving in case any other instance succeeded to save its own new 
attribute meantime.

What am I overlooking, how can this pattern fail?

In fact, to decrease the probability of the optimistic locking exception, I 
force re-fetch (in my new multi-instance code, not the single-instance old 
one), like this:

===
OSC.lock() // precisely same as above
try {
  ERXEC tempec=ERXEC.newEditingContext()
  tempec.fetchTimestamp=System.currentTimeMillis() // due to this, I don't need 
ec.unlock(); ec.lock(), for...
  def tempeo=eo.localInstanceIn(tempec)
  def rel=tempeo.someRelationship()  // ... whatever was cached in ECs, current 
FK from eo's table gets fetched now
  def attr=rel.someAttribute()   // ... just like its attribute from rel 
target's table
  if (TEST(attr,newAttributeValue)) { // precisely same as above (the only 
difference is that the values are newer...
def new=EOUtilities.createAndInsertInstance(tempec,'SomeRelationshipTarget')
new.setSomeAttribute(newAttributeValue)
tempeo.addObjectToBothSidesOfRelationshipWithKey(new,'someRelationship')
tempec.saveChanges() // ... and thus the probability of optimistic locking 
exception (caused by different relationship FK) is smaller (though of course 
not zero)
  }
} finally {
  OSC.unlock()
}
===

Are even these patters unsafe? Why?

Thanks a big lot,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-20 Thread Chuck Hill


On 2015-02-20, 12:30 PM, Ramsey Gurley wrote:


On Feb 20, 2015, at 12:09 PM, OC o...@ocs.czmailto:o...@ocs.cz wrote:

it must pass a slightly more complex test than uniqueness:
- when inserted into PRICE_OFFERS table,
- the newly inserted object must have value in column PRICE, which is higher,
- than any (max) of the already existing objects in that table, which
- have YES in a boolean column VALID, and
- at the same moment, have same value in AUCTION_ID column as the inserted 
object.
(In fact the real condition is even more complex, but this is the gist of it: 
consider an auction system, where a new bid added to a particular auction must 
be higher than all previous valid bids for the same auction.)
Nevertheless, I believe that when we are pursuing the 
implement-the-behaviour-at-the-application-level way (unlike the check 
restraint at the DB level), the particular TEST is actually irrelevant. The 
gist is that it must not be possible to store an object which does not pass 
TEST -- whatever the TEST tests.

Why is this a requirement? If highest price wins, then you only need to select 
the max price row where price offer date is less than auction end. If a few 
offers get thrown in there out of order, how does that break anything?

That seems to make sense.  You can save the bid, whatever it is, then 
immediately fetch any higher bid.  If there was one, show the user an you were 
just outbid message.

Chuck


See please again the [1] above -- the code must make sure that
(a) when TESTing, the participating objects are a proper snapshot of database 
contents of some moment in the past
(b) when saving, the code must make sure that if the values of the snapshot did 
change, the saving won't happen
That should be sufficient, should it not?
Does it make sense?
Thanks a lot,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-20 Thread Chuck Hill


On 2015-02-20, 9:27 AM, OC wrote:

Chuck,

Actually _this_ should not be weird, this should be quite a normal code; the 
only requirement is that check-and-save, i.e., conceptually,
===
if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
   def 
new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
   new.someAttribute=newAttributeValue
   eo.editingContext().saveChanges()
}
===
so that I can be absolutely sure that nobody stores an attribute value which -- 
at the moment of COMMITTing the appropriate INSERT -- would not pass the TEST

Alas, since the TEST is (slightly) more complex than A!=B, I can't use a UNIQUE 
db restraint.

One way would be to twist the DB to do the complete restraint to me, something 
like to a pseudo-code inserted_row.attr=MAX(SELECT attr FROM this_table WHERE 
another_attr.isValid) AND couple more similar conditions -- frankly I am not 
sure whether the database (FrontBase) can do that at all, and if it can, 
definitely I don't know how to.

I think you can do it as a Check constraint.  It needs to be a boolean 
expression, but I am not sure it can include selects.  A quick experiment would 
show if it can.



Nevertheless this is an interesting idea which I am going to pursue (if anybody 
happens to know the solution, either how to, or that it is not possible at all, 
of course I'll be grateful for an advice, before I dive into that).

Another way, the one I've tried to exploit so far, was implement the behaviour 
app-side:

I believe that is the only way to absolutely ensure this.  I don't think you 
can make a rock-solid guarantee from the app code level.

Can't I? That's bad.

So far, I thought this very simple concept should be rock-solid, but probably I 
am overlooking something of importance, as so often:

=== eo's entity locks on the someRelationship FK (among others) ===
OSC.lock() // possibly superfluous; simplifies situation by serializing 
intra-instance
try {
  ec.unlock(); ec.lock() //* to make sure we get changes from other ECs now, by 
your excellent advice
  def rel=eo.someRelationship() // in DB there might be a newer value (saved 
meantime by another instance)...
  def attr=rel.someAttribute()  // ... but it is not possible that in DB is an 
_older_ value than this
  if (TEST(attr,newAttributeValue)) {
def 
new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
new.setSomeAttribute(newAttributeValue) // once set, I NEVER change this 
value
eo.addObjectToBothSidesOfRelationshipWithKey(new,'someRelationship')
eo.editingContext().saveChanges() // catching optimistic exceptions and 
repeating the process if they happen
  }
} finally {
  OSC.unlock()
}
===

My reasoning is that
- intra-instance, consistency is ensured by locked (single) OSC and by //* -- I 
am sure that I see the latest eo.someRelationship and its rel.someAttribute 
before saving, and thus the TEST is reliable; my own instance, even with 
concurrent requests, can't do anything wrong
- inter-instance, the optimistic locking based on someRelationship FK should 
prevent saving in case any other instance succeeded to save its own new 
attribute meantime.

What am I overlooking, how can this pattern fail?

I am not quite following the requirements.  Is it unique only for this object 
or for all objects?

Chuck



In fact, to decrease the probability of the optimistic locking exception, I 
force re-fetch (in my new multi-instance code, not the single-instance old 
one), like this:

===
OSC.lock() // precisely same as above
try {
  ERXEC tempec=ERXEC.newEditingContext()
  tempec.fetchTimestamp=System.currentTimeMillis() // due to this, I don't need 
ec.unlock(); ec.lock(), for...
  def tempeo=eo.localInstanceIn(tempec)
  def rel=tempeo.someRelationship()  // ... whatever was cached in ECs, current 
FK from eo's table gets fetched now
  def attr=rel.someAttribute()   // ... just like its attribute from rel 
target's table
  if (TEST(attr,newAttributeValue)) { // precisely same as above (the only 
difference is that the values are newer...
def new=EOUtilities.createAndInsertInstance(tempec,'SomeRelationshipTarget')
new.setSomeAttribute(newAttributeValue)
tempeo.addObjectToBothSidesOfRelationshipWithKey(new,'someRelationship')
tempec.saveChanges() // ... and thus the probability of optimistic locking 
exception (caused by different relationship FK) is smaller (though of course 
not zero)
  }
} finally {
  OSC.unlock()
}
===

Are even these patters unsafe? Why?

Thanks a big lot,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Re: Never save objects which don't pass a test (was: searching for a weird deletion)

2015-02-20 Thread OC
Chuck,

On 20. 2. 2015, at 19:37, Chuck Hill ch...@gevityinc.com wrote:

 Actually _this_ should not be weird, this should be quite a normal code; the 
 only requirement is that check-and-save, i.e., conceptually,
 === [1]
 if (TEST(eo.someRelationship().someAttribute(),newAttributeValue)) {
def 
 new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
new.someAttribute=newAttributeValue
eo.editingContext().saveChanges()
 }
 ===
 so that I can be absolutely sure that nobody stores an attribute value which 
 -- at the moment of COMMITTing the appropriate INSERT -- would not pass the 
 TEST

...

 I believe that is the only way to absolutely ensure this.  I don’t think 
 you can make a rock-solid guarantee from the app code level.
 
 Can't I? That's bad.
 
 So far, I thought this very simple concept should be rock-solid, but 
 probably I am overlooking something of importance, as so often:
 
 === eo's entity locks on the someRelationship FK (among others) ===
 OSC.lock() // possibly superfluous; simplifies situation by serializing 
 intra-instance
 try {
   ec.unlock(); ec.lock() //* to make sure we get changes from other ECs now, 
 by your excellent advice
   def rel=eo.someRelationship() // in DB there might be a newer value (saved 
 meantime by another instance)...
   def attr=rel.someAttribute()  // ... but it is not possible that in DB is 
 an _older_ value than this
   if (TEST(attr,newAttributeValue)) {
 def 
 new=EOUtilities.createAndInsertInstance(eo.editingContext(),'SomeRelationshipTarget')
 new.setSomeAttribute(newAttributeValue) // once set, I NEVER change this 
 value
 eo.addObjectToBothSidesOfRelationshipWithKey(new,'someRelationship')
 eo.editingContext().saveChanges() // catching optimistic exceptions and 
 repeating the process if they happen
   }
 } finally {
   OSC.unlock()
 }
 ===
 
 My reasoning is that
 - intra-instance, consistency is ensured by locked (single) OSC and by //* 
 -- I am sure that I see the latest eo.someRelationship and its 
 rel.someAttribute before saving, and thus the TEST is reliable; my own 
 instance, even with concurrent requests, can't do anything wrong
 - inter-instance, the optimistic locking based on someRelationship FK should 
 prevent saving in case any other instance succeeded to save its own new 
 attribute meantime.
 
 What am I overlooking, how can this pattern fail?
 
 I am not quite following the requirements.  Is it unique only for this object 
 or for all objects?

Sorry I have mislead you by using the term “UNIQUE“. It does not have to be 
unique; it must pass a slightly more complex test than uniqueness:

- when inserted into PRICE_OFFERS table,
- the newly inserted object must have value in column PRICE, which is higher,
- than any (max) of the already existing objects in that table, which
- have YES in a boolean column VALID, and
- at the same moment, have same value in AUCTION_ID column as the inserted 
object.

(In fact the real condition is even more complex, but this is the gist of it: 
consider an auction system, where a new bid added to a particular auction must 
be higher than all previous valid bids for the same auction.)

Nevertheless, I believe that when we are pursuing the 
implement-the-behaviour-at-the-application-level way (unlike the check 
restraint at the DB level), the particular TEST is actually irrelevant. The 
gist is that it must not be possible to store an object which does not pass 
TEST -- whatever the TEST tests.

See please again the [1] above -- the code must make sure that

(a) when TESTing, the participating objects are a proper snapshot of database 
contents of some moment in the past
(b) when saving, the code must make sure that if the values of the snapshot did 
change, the saving won't happen

That should be sufficient, should it not?

Does it make sense?

Thanks a lot,
OC


 ___
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list  (Webobjects-dev@lists.apple.com)
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/webobjects-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com