Skip to content

Conversation

jonludlam
Copy link
Contributor

This allows the DB add_to_map calls to be idempotent. So if a map contains:

[('key','value')]

then

add_to_map(x,'key','value')

will succeed if idempotency is enabled, and fail with Duplicate_key if not.

add_to_map(x,'key','value2')

will always fail with a Duplicate_key exception.

By default, this behaviour is turned on when xapi uses the remote DB access from slaves, but turned off on the master. This has the effect of ensuring that the Xen API calls retain their current semantics for now.

The behaviour can be normalised across master/slave via the config file parameter

db_idempotent_map

which currently defaults to false. This default will change at some point in the future.

Signed-off-by: Jon Ludlam <jonathan.ludlam@citrix.com>
Signed-off-by: Jon Ludlam <jonathan.ludlam@citrix.com>
* Xapi code must assume idempotency of 'add_to_<map>' calls
* Remote DB calls will _always_ be idempotent
* DB calls on the master are controlled by the switch, defaulting to
  the original behaviour (non idempotent)
* API calls follow the master DB behaviour

Signed-off-by: Jon Ludlam <jonathan.ludlam@citrix.com>
@lindig
Copy link
Contributor

lindig commented Apr 14, 2017

Minor quibble: db_idempotent_map - not the map is idempotent but its add operation. Would call it db_map_add_idempotent = true/false.

@jonludlam
Copy link
Contributor Author

jonludlam commented Apr 14, 2017

all map operations are idempotent after this c/s, because 'remove' already was.

Copy link
Contributor

@lindig lindig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small comments.

let t = Schema.Value.Unsafe_cast.pairs t in
if List.mem key (List.map fst t) then raise Duplicate;
Schema.Value.Pairs ((key, value) :: t)
if List.mem_assoc key t && (not idempotent || List.assoc key t <> value) then raise Duplicate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for performance it would be best to check the cheapest conditions first - that would be idempotent and List.assoc next (I assume).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's quite so clear cut here - checking the idempotent condition is only relevant if the mem_assoc test is true, and that's expected to be quite rare. Also, in the default case here idempotent is false, hence we don't do the List.assoc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right - I now realise that you do List.mem to avoid a Not_found exception. This can stay as it is. But I think the best performance would have this:

try if not idempotent && List.assoc key t <> value then raise Duplicate 
with Not_found -> ()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't have the same semantics - it doesn't raise any exception if idempotent is true, which isn't the same as the current change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last try - not better performance but easier to read maybe:

if List.mem_assoc key t &&  idempotent && List.assoc key t = value then raise Duplicate;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that has wrong semantics too.
If idempotent then we shouldn't raise an exception when List.assoc key t = value,
and otherwise we should raise an exception if List.mem_assoc key t regardless of the value.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I wanted to use: !a || !b = !(a && b) but forgot the outer negation. So it probably should stay the way it is.


exception Duplicate
let add_to_map key value t =
let add_to_map idempotent key value t =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd consider givingidempotent a label because at a call site true/false isn't very descriptive and easy to mix up. You could even provide a default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Suggested by @lindig in PR#3001

Signed-off-by: Jon Ludlam <jonathan.ludlam@citrix.com>
@jonludlam jonludlam merged commit 825884d into xapi-project:master Apr 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants