Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yet another sharding support implementation #1025

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3f37a88
Sharding metadata, ShardKey annotation support
clslrns Jan 27, 2015
161cb1b
Add ensureSharding + some auxiliary methods to SchemaManager
clslrns Jan 29, 2015
ea8487f
DocumentPersister now uses shard key criteria
clslrns Jan 29, 2015
c93b7cc
Workaround replication lag in tests
clslrns Jan 30, 2015
f6abf0b
Rename ShardKey 'fields' to 'keys' to conform to index definition
clslrns Jan 30, 2015
cd3479e
Improve exception text
clslrns Jan 30, 2015
aff168f
Add getFieldMappingByDbFieldName to ClassMetadataInfo
clslrns Jan 30, 2015
4705907
Modify DocumentPersister and SchemaManager to support index-like shar…
clslrns Jan 30, 2015
6f0b0f0
Set shard key after all fields
clslrns Jan 30, 2015
08d0cef
Fix persister's update with empty fields and shard key change protection
clslrns Jan 30, 2015
51ab064
Fix tests according to the new shard key description format
clslrns Jan 30, 2015
fca3a59
Add failing remove and refresh tests
clslrns Feb 2, 2015
5a94526
Remove workarounds in tests
clslrns Feb 2, 2015
47439d4
Shard key aware remove and refresh
clslrns Feb 2, 2015
53a65ae
Add ensureSharding tests
clslrns Feb 2, 2015
4f81cf1
ensureSharding creates an index for collections with documents
clslrns Feb 2, 2015
5520c22
Refactor criteria in executeUpsert
clslrns Feb 2, 2015
198982f
Fix tests, add group replication_lag
clslrns Feb 2, 2015
a907b00
Protect from shard key change during update with all possible upsert …
clslrns Feb 3, 2015
92d4f74
Test serialization of shard key
clslrns Feb 4, 2015
67c729e
Shard key inheritance tests
clslrns Feb 4, 2015
e284858
Shard key support for XmlDriver
clslrns Feb 4, 2015
ccbfda3
Shard key support for YamlDriver
clslrns Feb 4, 2015
471a998
Fix shard key options parsing in XmlDriver
clslrns Feb 4, 2015
6427701
Add shard-key attribute to document type in XSD
clslrns Feb 4, 2015
e1163d0
Fix XSD
clslrns Feb 4, 2015
d79830a
Add options to ensureSharding
clslrns Feb 4, 2015
8238b76
Make doc block for shardKey property more understandable
clslrns Feb 10, 2015
ae0c634
Rename ensureSharding's $options to $indexOptions to make its purpose…
clslrns Feb 10, 2015
6bc01fd
Options for shard key could be specified in XML attributes
clslrns Feb 10, 2015
2ce5240
Bring back id argument, deprecate it
clslrns Oct 18, 2015
415f19d
Forbid increment fields to be part of the shard key
clslrns Oct 18, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions doctrine-mongo-mapping.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
<xs:element name="lifecycle-callbacks" type="odm:lifecycle-callbacks" minOccurs="0"/>
<xs:element name="also-load-methods" type="odm:also-load-methods" minOccurs="0"/>
<xs:element name="indexes" type="odm:indexes" minOccurs="0"/>
<xs:element name="shard-key" type="odm:shard-key" minOccurs="0"/>
</xs:sequence>
<xs:attribute name="db" type="xs:NMTOKEN" />
<xs:attribute name="name" type="xs:string" />
Expand All @@ -60,7 +61,7 @@
<xs:complexType name="field">
<xs:sequence>
<xs:element name="id-generator-option" type="odm:id-generator-option" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:sequence>
<xs:attribute name="id" type="xs:boolean" default="false" />
<xs:attribute name="name" type="xs:NMTOKEN" />
<xs:attribute name="type" type="xs:NMTOKEN" default="string" />
Expand All @@ -82,7 +83,7 @@
<xs:attribute name="sparse" type="xs:boolean" />
<xs:attribute name="unique" type="xs:boolean" />
</xs:complexType>

<xs:complexType name="id-generator-option">
<xs:attribute name="name" type="xs:NMTOKEN" use="required"/>
<xs:attribute name="value" type="xs:string" use="required"/>
Expand Down Expand Up @@ -275,6 +276,24 @@
</xs:sequence>
</xs:complexType>

<xs:complexType name="shard-key">
<xs:sequence>
<xs:element name="key" type="odm:shard-key-key" minOccurs="1" maxOccurs="unbounded"/>
<xs:element name="option" type="odm:shard-key-option" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="unique" type="xs:boolean"/>
<xs:attribute name="numInitialChunks" type="xs:integer"/>
</xs:complexType>

<xs:complexType name="shard-key-key">
<xs:attribute name="name" type="xs:NMTOKEN" use="required"/>
<xs:attribute name="order" type="xs:NMTOKEN" default="asc" />
</xs:complexType>

<xs:complexType name="shard-key-option">
<xs:attribute name="name" type="xs:NMTOKEN" use="required"/>
<xs:attribute name="value" type="xs:NMTOKEN" use="required" />
</xs:complexType>

<xs:complexType name="also-load-method">
<xs:attribute name="method" type="xs:NMTOKEN" use="required"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,4 @@
require_once __DIR__ . '/PostLoad.php';
require_once __DIR__ . '/PreFlush.php';
require_once __DIR__ . '/HasLifecycleCallbacks.php';
require_once __DIR__ . '/ShardKey.php';
1 change: 1 addition & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ final class Document extends AbstractDocument
public $repositoryClass;
public $indexes = array();
public $requireIndexes = false;
public $shardKey;
public $slaveOkay;
}
30 changes: 30 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ShardKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/

namespace Doctrine\ODM\MongoDB\Mapping\Annotations;

use Doctrine\Common\Annotations\Annotation;

/** @Annotation */
final class ShardKey extends Annotation
{
public $keys = array();
public $unique;
public $numInitialChunks;
}
2 changes: 1 addition & 1 deletion lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
namespace Doctrine\ODM\MongoDB\Mapping;

use Doctrine\Instantiator\Instantiator;
use Doctrine\ODM\MongoDB\LockException;

/**
* A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
Expand Down Expand Up @@ -115,6 +114,7 @@ public function __sleep()
'generatorOptions',
'idGenerator',
'indexes',
'shardKey',
);

// The rest of the metadata is only serialized if necessary.
Expand Down
17 changes: 17 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonS
$this->addInheritedFields($class, $parent);
$this->addInheritedRelations($class, $parent);
$this->addInheritedIndexes($class, $parent);
$this->setInheritedShardKey($class, $parent);
$class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField);
Expand Down Expand Up @@ -336,4 +337,20 @@ private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $par
$subClass->addIndex($index['keys'], $index['options']);
}
}

/**
* Adds inherited shard key to the subclass mapping.
*
* @param ClassMetadata $subClass
* @param ClassMetadata $parentClass
*/
private function setInheritedShardKey(ClassMetadata $subClass, ClassMetadata $parentClass)
{
if ($parentClass->isSharded()) {
$subClass->setShardKey(
$parentClass->shardKey['keys'],
$parentClass->shardKey['options']
);
}
}
}
103 changes: 93 additions & 10 deletions lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@

namespace Doctrine\ODM\MongoDB\Mapping;

use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
use Doctrine\ODM\MongoDB\LockException;
use Doctrine\ODM\MongoDB\Mapping\MappingException;
use Doctrine\ODM\MongoDB\Proxy\Proxy;
use Doctrine\ODM\MongoDB\Types\Type;
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
use InvalidArgumentException;

/**
Expand Down Expand Up @@ -194,6 +193,11 @@ class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMet
*/
public $indexes = array();

/**
* READ-ONLY: Keys and options describing shard key. Only for sharded collections.
*/
public $shardKey;

/**
* READ-ONLY: Whether or not queries on this document should require indexes.
*/
Expand Down Expand Up @@ -518,7 +522,7 @@ public function setCustomRepositoryClass($repositoryClassName)
if ($this->isEmbeddedDocument) {
return;
}

if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
$repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
}
Expand Down Expand Up @@ -798,6 +802,67 @@ public function hasIndexes()
return $this->indexes ? true : false;
}

/**
* Set shard key for this Document.
*
* @param array $keys Array of document keys.
* @param array $options Array of sharding options.
*
* @throws MappingException
*/
public function setShardKey(array $keys, array $options = array())
{
if ($this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
}

if ($this->isEmbeddedDocument) {
throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
}

foreach ($keys as $field) {
if ($this->getTypeOfField($field) == 'increment') {
throw MappingException::noIncrementFieldsAllowedInShardKey($this->getName());
}
}

$this->shardKey = array(
'keys' => array_map(function($value) {
if ($value == 1 || $value == -1) {
return (int) $value;
}
if (is_string($value)) {
$lower = strtolower($value);
if ($lower === 'asc') {
return 1;
} elseif ($lower === 'desc') {
return -1;
}
}
return $value;
}, $keys),
'options' => $options
);
}

/**
* @return array
*/
public function getShardKey()
{
return $this->shardKey;
}

/**
* Checks whether this document has shard key or not.
*
* @return bool
*/
public function isSharded()
{
return $this->shardKey ? true : false;
}

/**
* Sets the change tracking policy used by this class.
*
Expand Down Expand Up @@ -1112,7 +1177,7 @@ public function mapField(array $mapping)
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
$mapping['isCascadeDetach'] = in_array('detach', $cascades);

if (isset($mapping['type']) && $mapping['type'] === 'file') {
$mapping['file'] = true;
}
Expand Down Expand Up @@ -1160,7 +1225,7 @@ public function mapField(array $mapping)
&& ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
}

if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
}
Expand Down Expand Up @@ -1514,7 +1579,7 @@ public function setFieldValue($document, $field, $value)
//so the proxy needs to be loaded first.
$document->__load();
}

$this->reflFields[$field]->setValue($document, $value);
}

Expand All @@ -1531,7 +1596,7 @@ public function getFieldValue($document, $field)
if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
$document->__load();
}

return $this->reflFields[$field]->getValue($document);
}

Expand All @@ -1543,8 +1608,6 @@ public function getFieldValue($document, $field)
* @return array The field mapping.
*
* @throws MappingException if the $fieldName is not found in the fieldMappings array
*
* @throws MappingException
*/
public function getFieldMapping($fieldName)
{
Expand All @@ -1554,6 +1617,26 @@ public function getFieldMapping($fieldName)
return $this->fieldMappings[$fieldName];
}

/**
* Gets the field mapping by its DB name.
* E.g. it returns identifier's mapping when called with _id.
*
* @param string $dbFieldName
*
* @return array
* @throws MappingException
*/
public function getFieldMappingByDbFieldName($dbFieldName)
{
foreach ($this->fieldMappings as $mapping) {
if ($mapping['name'] == $dbFieldName) {
return $mapping;
}
}

throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
}

/**
* Check if the field is not null.
*
Expand Down Expand Up @@ -1701,7 +1784,7 @@ public function isIdGeneratorNone()
* value to use depending on the column type.
*
* @param array $mapping The version field mapping array
*
*
* @throws LockException
*/
public function setVersionMapping(array &$mapping)
Expand Down
24 changes: 24 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ public function loadMetadataForClass($className, ClassMetadata $class)
}
}

// Set shard key after all fields to ensure we mapped all its keys
if (isset($classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey'])) {
$this->setShardKey($class, $classAnnotations['Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey']);
}

/** @var $method \ReflectionMethod */
foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
/* Filter for the declaring class only. Callbacks from parent
Expand Down Expand Up @@ -240,6 +245,25 @@ private function addIndex(ClassMetadataInfo $class, $index, array $keys = array(
$class->addIndex($keys, $options);
}

/**
* @param ClassMetadataInfo $class
* @param ODM\ShardKey $shardKey
*
* @throws MappingException
*/
private function setShardKey(ClassMetadataInfo $class, ODM\ShardKey $shardKey)
{
$options = array();
$allowed = array('unique', 'numInitialChunks');
foreach ($allowed as $name) {
if (isset($shardKey->$name)) {
$options[$name] = $shardKey->$name;
}
}

$class->setShardKey($shardKey->keys, $options);
}

/**
* Factory method for the Annotation Driver
*
Expand Down
Loading