Skip to content

Conflict resolution strategy #54

@FabioBatSilva

Description

@FabioBatSilva

In order to make the API easier to use would be nice to have conflict resolution strategies..

  • Add attribute Riak\Input\PutInput#conflictResolver
  • Add attribute Riak\Bucket#conflictResolver
  • Add method Riak\Output\Output#getObject()
  • Add interface Riak\Output\Resolver\ConflictResolver
  • Add exception Riak\NonUniqueException
  • Add exception Riak\UnresolvedConflictException
API Changes :
<?php

namespace Riak\Output;

class PutOutput 
{
    // ...
    /** 
    * Retrieves a unique riak object
    * 
    * If this output contain more than one object 
    * it will try to resolve its siblings using a ConflictResolver specified by PutInput or Bucket
    * 
    * if there are no ConflictResolver configured for its Bucket nor PutInput it will throws a NonUniqueException
    * otherwise the ConflictResolver will be responsible for resolving the siblings into a single object.
    *
    * @return \Riak\Object
    *
    * @throws \Riak\NotFoundException   If this output contain none objects
    * @throws \Riak\NonUniqueException  If this output contain more than one object
    * @throws \Riak\UnresolvedConflictException If the ConflictResolver cannot determine a single object.
    */
    public function getObject(){}
}

namespace Riak\Output\Resolver;

interface ConflictResolver 
{
    /** 
    * Applies encoded logic to produce a single value from a list of siblings.
    *
    * @param \Riak\Bucket              $bucket
    * @param \Riak\Output\Output       $output
    * @param \Riak\Input\PutInput|null $putInput
    *
    * @return \Riak\Object
    *
    * @throws \Riak\UnresolvedConflictException If it cannot determine a single value.
    */
    public function resolve(Bucket $bucket, Output $output, PutInput $putInput = null);
}
Some basic built-in conflict resolver
  • FirstSiblingResolver - Resolve the conflict using the first object in Output#objectlist
  • LastSiblingResolver - Resolve the conflict using the last object in Output#objectlist
API Usage :

Manually resolving siblings

<?php
$props  = new \Riak\BucketPropertyList($nVal = 3, $allowMult = true);
$bucket = new \Riak\Bucket($client, 'test_bucket');
$output = null;

try {
    $output = $bucket->get($key);
    $object = $output->getObject();
} catch (\Riak\NotFoundException $e) {
    // Not found 
} catch (\Riak\NonUniqueException $e) {
    $winner   = doSomeMagicAndResolveTheWinner();
    $options  = new \Riak\Input\PutInput();

    $options->setVClock($output->getVClock());
    $bucket->put($winner, $options);

    $output = $winner;
}

Auto resolving sibling using the last object

<?php
$props   = new \Riak\BucketPropertyList($nVal = 3, $allowMult = true);
$bucket  = new \Riak\Bucket($client, 'test_bucket');

// Configure a default resolver
$props->setConflictResolver(new LastSiblingResolver());
$bucket->setPropertyList($props);

$output = $bucket->get($key);
// this $output contain more than one object
echo $output->hasSiblings(); 
// Resolve the conflict using the LastSiblingResolver
$object = $output->getObject();

Auto resolving a specific key using a custom ConflictResolver

<?php

class MyResolver implements ConflictResolver
{
    /**
     * {@inheritdoc}
     */
    public function resolve(Bucket $bucket, Output $output, PutInput $putInput = null) 
    {
         $winner   = doSomeMagicAndResolveTheWinner();
         $options  = new \Riak\Input\PutInput();

         $options->setVClock($output->getVClock());
         $bucket->put($winner, $options);

         return $winner;
    }
}

$props   = new \Riak\BucketPropertyList($nVal = 3, $allowMult = true);
$options = \Riak\Input\GetInput();

// Configure a resolver for a specific get operation
$options->setConflictResolver(new MyResolver());
$bucket->setPropertyList($props);

$output = $bucket->get($key, $options);
// this $output contain more than one object
echo $output->hasSiblings(); 
// Resolve the conflict using MyResolver
$object = $output->getObject();

If you guys agree with this approach I'll be happy to prepare a PR ;)

PS: We could probably use a similar approach to handle retry policies during get/put operations..

Cheers ..

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions