diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php
index c984840e2067daa0f43f06319e9c3a68b1287012..6e432447263ee9d4d6240a6c104b6c599db1e81c 100644
--- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php
+++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Save.php
@@ -101,6 +101,9 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Set
             }
             $model->save();
             $this->messageManager->addSuccess(__('You saved the attribute set.'));
+        } catch (\Magento\Framework\Exception\AlreadyExistsException $e) {
+            $this->messageManager->addErrorMessage($e->getMessage());
+            $hasError = true;
         } catch (\Magento\Framework\Exception\LocalizedException $e) {
             $this->messageManager->addError($e->getMessage());
             $hasError = true;
diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php
index 327729cf83695931a8c378dc91f41ab66a9e7d69..290770bd296c3bf9ef8854de86008674776ae2b7 100644
--- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php
+++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php
@@ -73,9 +73,12 @@ class Sku extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
     {
         $attribute = $this->getAttribute();
         $entity = $attribute->getEntity();
-        $increment = $this->_getLastSimilarAttributeValueIncrement($attribute, $object);
         $attributeValue = $object->getData($attribute->getAttributeCode());
+        $increment = null;
         while (!$entity->checkAttributeUniqueValue($attribute, $object)) {
+            if ($increment === null) {
+                $increment = $this->_getLastSimilarAttributeValueIncrement($attribute, $object);
+            }
             $sku = trim($attributeValue);
             if (strlen($sku . '-' . ++$increment) > self::SKU_MAX_LENGTH) {
                 $sku = substr($sku, 0, -strlen($increment) - 1);
diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php
index f17760237034041764539e533d5f1440b8dad3e4..cbf0464ca366158d4d3e4d75f65e4435f0834502 100644
--- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php
@@ -58,6 +58,11 @@ class CreateHandler implements ExtensionInterface
      */
     protected $fileStorageDb;
 
+    /**
+     * @var array
+     */
+    private $mediaAttributeCodes;
+
     /**
      * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
      * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
@@ -145,9 +150,11 @@ class CreateHandler implements ExtensionInterface
         }
 
         /* @var $mediaAttribute \Magento\Catalog\Api\Data\ProductAttributeInterface */
-        foreach ($this->mediaConfig->getMediaAttributeCodes() as $mediaAttrCode) {
+        foreach ($this->getMediaAttributeCodes() as $mediaAttrCode) {
             $attrData = $product->getData($mediaAttrCode);
-
+            if (empty($attrData) && empty($clearImages) && empty($newImages) && empty($existImages)) {
+                continue;
+            }
             if (in_array($attrData, $clearImages)) {
                 $product->setData($mediaAttrCode, 'no_selection');
             }
@@ -394,4 +401,17 @@ class CreateHandler implements ExtensionInterface
             );
         }
     }
+
+    /**
+     * Get Media Attribute Codes cached value
+     *
+     * @return array
+     */
+    private function getMediaAttributeCodes()
+    {
+        if ($this->mediaAttributeCodes === null) {
+            $this->mediaAttributeCodes = $this->mediaConfig->getMediaAttributeCodes();
+        }
+        return $this->mediaAttributeCodes;
+    }
 }
diff --git a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php
index 3df4d9813a668b6fc81d7706412e1cf4e671c5e5..167dc2be15b292c4fabd9983911cd54cba89e62c 100644
--- a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php
+++ b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php
@@ -40,7 +40,6 @@ class SaveHandler
         Link $linkResource,
         ProductLinkRepositoryInterface $productLinkRepository
     ) {
-
         $this->metadataPool = $metadataPool;
         $this->linkResource = $linkResource;
         $this->productLinkRepository = $productLinkRepository;
@@ -54,12 +53,18 @@ class SaveHandler
      */
     public function execute($entityType, $entity)
     {
-        /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/
-        foreach ($this->productLinkRepository->getList($entity) as $link) {
-            $this->productLinkRepository->delete($link);
+        $link = $entity->getData($this->metadataPool->getMetadata($entityType)->getLinkField());
+        if ($this->linkResource->hasProductLinks($link)) {
+            /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/
+            foreach ($this->productLinkRepository->getList($entity) as $link) {
+                $this->productLinkRepository->delete($link);
+            }
         }
-        foreach ($entity->getProductLinks() as $link) {
-            $this->productLinkRepository->save($link);
+        $productLinks = $entity->getProductLinks();
+        if (count($productLinks) > 0) {
+            foreach ($entity->getProductLinks() as $link) {
+                $this->productLinkRepository->save($link);
+            }
         }
         return $entity;
     }
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
index 407c2027923de4c683272773d519cf1adc7b1917..e743c5d384bdd539a15c4f666b36ce0f43f7f4d0 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php
@@ -238,7 +238,9 @@ class Category extends AbstractResource
         if (!$object->getChildrenCount()) {
             $object->setChildrenCount(0);
         }
-
+        $object->setAttributeSetId(
+            $object->getAttributeSetId() ?: $this->getEntityType()->getDefaultAttributeSetId()
+        );
         if ($object->isObjectNew()) {
             if ($object->getPosition() === null) {
                 $object->setPosition($this->_getMaxPosition($object->getPath()) + 1);
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
index 99c4316d20740edc4d3d6415bbfb06a344e7e5c7..8e93868dc7e51916a1c44997ad98045edbe1bd4f 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
@@ -351,14 +351,11 @@ class Attribute extends \Magento\Eav\Model\Entity\Attribute implements
      */
     public function getApplyTo()
     {
-        if ($this->getData(self::APPLY_TO)) {
-            if (is_array($this->getData(self::APPLY_TO))) {
-                return $this->getData(self::APPLY_TO);
-            }
-            return explode(',', $this->getData(self::APPLY_TO));
-        } else {
-            return [];
+        $applyTo = $this->_getData(self::APPLY_TO) ?: [];
+        if (!is_array($applyTo)) {
+            $applyTo = explode(',', $applyTo);
         }
+        return $applyTo;
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php
index bbccabe5a61c969f74294398ad484e1b0d3c64f9..17bfa90e8842d4dc511d8d78f2dc23151a3934b3 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php
@@ -96,6 +96,30 @@ class Link extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         return $connection->fetchOne($select, $bind);
     }
 
+    /**
+     * Check if product has links.
+     *
+     * @param int $parentId ID of product
+     * @return bool
+     */
+    public function hasProductLinks($parentId)
+    {
+        $connection = $this->getConnection();
+        $select = $connection->select()->from(
+            $this->getMainTable(),
+            ['count' => new \Zend_Db_Expr('COUNT(*)')]
+        )->where(
+            'product_id = :product_id'
+        ) ;
+
+        return $connection->fetchOne(
+            $select,
+            [
+                'product_id' => $parentId
+            ]
+        ) > 0;
+    }
+
     /**
      * Save Product Links process
      *
diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php
index c5cdd1950cc3803a238bdd77caa1ee5fa2f8b88e..58e364920bb6ad4919212ba329e2d8294cd36027 100644
--- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php
+++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php
@@ -15,13 +15,14 @@ use Magento\CatalogInventory\Model\Indexer\Stock\Processor;
 use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as StockItemResource;
 use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface;
 use Magento\CatalogInventory\Model\StockRegistryStorage;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\DB\MapperFactory;
 use Magento\Framework\DB\QueryBuilderFactory;
 use Magento\Framework\Exception\CouldNotDeleteException;
 use Magento\Framework\Exception\CouldNotSaveException;
 use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
 use Magento\Framework\Stdlib\DateTime\DateTime;
+use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
 
 /**
  * Class StockItemRepository
@@ -89,6 +90,9 @@ class StockItemRepository implements StockItemRepositoryInterface
      */
     protected $stockRegistryStorage;
 
+    /** @var  \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */
+    protected $productCollectionFactory;
+
     /**
      * @param StockConfigurationInterface $stockConfiguration
      * @param StockStateProviderInterface $stockStateProvider
@@ -129,6 +133,21 @@ class StockItemRepository implements StockItemRepositoryInterface
         $this->dateTime = $dateTime;
     }
 
+    /**
+     * @deprecated
+     * @return  \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory
+     */
+    private function getProductCollectionFactory()
+    {
+        if ($this->productCollectionFactory === null) {
+            $this->productCollectionFactory = ObjectManager::getInstance()->get(
+                \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class
+            );
+        }
+
+        return $this->productCollectionFactory;
+    }
+
     /**
      * @inheritdoc
      */
@@ -136,8 +155,12 @@ class StockItemRepository implements StockItemRepositoryInterface
     {
         try {
             /** @var \Magento\Catalog\Model\Product $product */
-            $product = $this->productFactory->create();
-            $product->load($stockItem->getProductId());
+            $product = $this->getProductCollectionFactory()->create()
+                ->setFlag('has_stock_status_filter')
+                ->addIdFilter($stockItem->getProductId())
+                ->addFieldToSelect('type_id')
+                ->getFirstItem();
+
             if (!$product->getId()) {
                 return $stockItem;
             }
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php
index 769e2db4cc84553a3a10e081e6785a02a72b5d15..23b7805e622e32938a3403e58af7f30d8a8d80e8 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\CatalogInventory\Test\Unit\Model\Stock;
 
+use Magento\Catalog\Model\ResourceModel\Product\Collection;
+use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
 use Magento\CatalogInventory\Model\Stock\StockItemRepository;
 use Magento\CatalogInventory\Api\Data as InventoryApiData;
 use Magento\CatalogInventory\Model\StockRegistryStorage;
@@ -153,9 +155,8 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->setMethods(['load', 'getId', 'getTypeId', '__wakeup'])
             ->getMock();
-        $this->productFactoryMock->expects($this->any())
-            ->method('create')
-            ->willReturn($this->productMock);
+
+        $this->productFactoryMock->expects($this->any())->method('create')->willReturn($this->productMock);
 
         $this->queryBuilderFactoryMock = $this->getMockBuilder(\Magento\Framework\DB\QueryBuilderFactory::class)
             ->setMethods(['create'])
@@ -185,6 +186,22 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
 
+        $productCollection = $this->getMockBuilder(
+            \Magento\Catalog\Model\ResourceModel\Product\Collection::class
+        )->disableOriginalConstructor()->getMock();
+
+        $productCollection->expects($this->any())->method('setFlag')->willReturnSelf();
+        $productCollection->expects($this->any())->method('addIdFilter')->willReturnSelf();
+        $productCollection->expects($this->any())->method('addFieldToSelect')->willReturnSelf();
+        $productCollection->expects($this->any())->method('getFirstItem')->willReturn($this->productMock);
+
+        $productCollectionFactory = $this->getMockBuilder(CollectionFactory::class)
+            ->setMethods(['create'])
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $productCollectionFactory->expects($this->any())->method('create')->willReturn($productCollection);
+
         $this->model = (new ObjectManager($this))->getObject(
             StockItemRepository::class,
             [
@@ -200,6 +217,7 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
                 'indexProcessor' => $this->indexProcessorMock,
                 'dateTime' => $this->dateTime,
                 'stockRegistryStorage' => $this->stockRegistryStorage,
+                'productCollectionFactory' => $productCollectionFactory,
             ]
         );
     }
@@ -263,7 +281,6 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
         $productId = 1;
 
         $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId);
-        $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf();
         $this->productMock->expects($this->once())->method('getId')->willReturn($productId);
         $this->productMock->expects($this->once())->method('getTypeId')->willReturn('typeId');
         $this->stockConfigurationMock->expects($this->once())->method('isQty')->with('typeId')->willReturn(true);
@@ -309,7 +326,6 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
         $productId = 1;
 
         $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId);
-        $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf();
         $this->productMock->expects($this->once())->method('getId')->willReturn(null);
         $this->stockRegistryStorage->expects($this->never())->method('removeStockItem');
         $this->stockRegistryStorage->expects($this->never())->method('removeStockStatus');
@@ -325,7 +341,6 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase
         $productId = 1;
 
         $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId);
-        $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf();
         $this->productMock->expects($this->once())->method('getId')->willReturn($productId);
         $this->productMock->expects($this->once())->method('getTypeId')->willReturn('typeId');
         $this->stockConfigurationMock->expects($this->once())->method('isQty')->with('typeId')->willReturn(false);
diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php
index ea24235b0fe2ef694e2087f7a8bdbf4c8ece3399..cb9a6b534609f7205f2c8a569b022389e13436b7 100644
--- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php
+++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php
@@ -32,7 +32,7 @@ class UpdateConfigurations
         'swatch_image',
         'small_image',
         'thumbnail',
-        'image'
+        'image',
     ];
 
     /**
@@ -65,13 +65,15 @@ class UpdateConfigurations
     ) {
         $configurations = $this->getConfigurations();
         $configurations = $this->variationHandler->duplicateImagesForVariations($configurations);
-        foreach ($configurations as $productId => $productData) {
-            /** @var \Magento\Catalog\Model\Product $product */
-            $product = $this->productRepository->getById($productId, false, $this->request->getParam('store', 0));
-            $productData = $this->variationHandler->processMediaGallery($product, $productData);
-            $product->addData($productData);
-            if ($product->hasDataChanges()) {
-                $product->save();
+        if (count($configurations)) {
+            foreach ($configurations as $productId => $productData) {
+                /** @var \Magento\Catalog\Model\Product $product */
+                $product = $this->productRepository->getById($productId, false, $this->request->getParam('store', 0));
+                $productData = $this->variationHandler->processMediaGallery($product, $productData);
+                $product->addData($productData);
+                if ($product->hasDataChanges()) {
+                    $product->save();
+                }
             }
         }
         return $configurableProduct;
@@ -91,6 +93,12 @@ class UpdateConfigurations
         }
 
         foreach ($configurableMatrix as $item) {
+            if (empty($item['was_changed'])) {
+                continue;
+            } else {
+                unset($item['was_changed']);
+            }
+
             if (!$item['newProduct']) {
                 $result[$item['id']] = $this->mapData($item);
 
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php
index 321870b96d32abf13c48fdc0975efc9ba5248f17..0d0bba60a7777c75deef4bc9e0a41720b9597163 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php
@@ -29,6 +29,9 @@ class VariationHandler
     /** @var \Magento\Catalog\Model\ProductFactory */
     protected $productFactory;
 
+    /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute[] */
+    private $attributes;
+
     /**
      * @var \Magento\CatalogInventory\Api\StockConfigurationInterface
      * @deprecated
@@ -70,6 +73,7 @@ class VariationHandler
     public function generateSimpleProducts($parentProduct, $productsData)
     {
         $generatedProductIds = [];
+        $this->attributes = null;
         $productsData = $this->duplicateImagesForVariations($productsData);
         foreach ($productsData as $simpleProductData) {
             $newSimpleProduct = $this->productFactory->create();
@@ -160,7 +164,10 @@ class VariationHandler
             $parentProduct->getNewVariationsAttributeSetId()
         );
 
-        foreach ($product->getTypeInstance()->getSetAttributes($product) as $attribute) {
+        if ($this->attributes === null) {
+            $this->attributes = $product->getTypeInstance()->getSetAttributes($product);
+        }
+        foreach ($this->attributes as $attribute) {
             if ($attribute->getIsUnique() ||
                 $attribute->getAttributeCode() == 'url_key' ||
                 $attribute->getFrontend()->getInputType() == 'gallery' ||
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php
index bed5c96b5216eb76344e9ce593ea56056be9a35b..def49f42fa960b738fe952be4a2d18bada61ffd2 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php
@@ -90,13 +90,24 @@ class UpdateConfigurationsTest extends \PHPUnit_Framework_TestCase
                 'swatch_image' => 'simple2_swatch_image',
                 'small_image' => 'simple2_small_image',
                 'thumbnail' => 'simple2_thumbnail',
-                'image' => 'simple2_image'
+                'image' => 'simple2_image',
+                'was_changed' => true,
             ],
             [
                 'newProduct' => false,
                 'id' => 'product3',
-                'qty' => '3'
-            ]
+                'qty' => '3',
+                'was_changed' => true,
+            ],
+            [
+                'newProduct' => false,
+                'id' => 'product4',
+                'status' => 'simple4_status',
+                'sku' => 'simple2_sku',
+                'name' => 'simple2_name',
+                'price' => '3.33',
+                'weight' => '5.55',
+            ],
         ];
         $configurations = [
             'product2' => [
@@ -118,8 +129,8 @@ class UpdateConfigurationsTest extends \PHPUnit_Framework_TestCase
         ];
         /** @var Product[]|\PHPUnit_Framework_MockObject_MockObject[] $productMocks */
         $productMocks = [
-            'product2' => $this->getProductMock($configurations['product2'], true),
-            'product3' => $this->getProductMock($configurations['product3'])
+            'product2' => $this->getProductMock($configurations['product2'], true, true),
+            'product3' => $this->getProductMock($configurations['product3'], false, true),
         ];
 
         $this->requestMock->expects(static::any())
@@ -161,26 +172,27 @@ class UpdateConfigurationsTest extends \PHPUnit_Framework_TestCase
      * @param bool $hasDataChanges
      * @return Product|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected function getProductMock(array $expectedData = null, $hasDataChanges = false)
+    protected function getProductMock(array $expectedData = null, $hasDataChanges = false, $wasChanged = false)
     {
         $productMock = $this->getMockBuilder(Product::class)
             ->disableOriginalConstructor()
             ->getMock();
 
-        if ($expectedData !== null) {
-            $productMock->expects(static::once())
-                ->method('addData')
-                ->with($expectedData)
+        if ($wasChanged !== false) {
+            if ($expectedData !== null) {
+                $productMock->expects(static::once())
+                    ->method('addData')
+                    ->with($expectedData)
+                    ->willReturnSelf();
+            }
+
+            $productMock->expects(static::any())
+                ->method('hasDataChanges')
+                ->willReturn($hasDataChanges);
+            $productMock->expects($hasDataChanges ? static::once() : static::never())
+                ->method('save')
                 ->willReturnSelf();
         }
-
-        $productMock->expects(static::any())
-            ->method('hasDataChanges')
-            ->willReturn($hasDataChanges);
-        $productMock->expects($hasDataChanges ? static::once() : static::never())
-            ->method('save')
-            ->willReturnSelf();
-
         return $productMock;
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
index ffabd9a8627dfca82da9b87ebd84c799086d9edf..c182d9f8216c09a42868eb487dfa06b0e718b5c7 100644
--- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
+++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js
@@ -391,11 +391,11 @@ define([
                     'small_image': row['small_image'],
                     image: row.image,
                     'thumbnail': row.thumbnail,
-                    'attributes': attributesText
+                    'attributes': attributesText,
+                    'was_changed': true
                 };
                 product[this.canEditField] = row.editable;
                 product[this.newProductField] = row.newProduct;
-
                 tmpArray.push(product);
             }, this);
 
diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
index 878ddf812028825267297aac958c22c14070f7b9..5a88c601e17e3c033040b082f8dfe47b83fd6a03 100644
--- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
+++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
@@ -13,7 +13,8 @@ use Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend;
 use Magento\Eav\Model\Entity\Attribute\Frontend\AbstractFrontend;
 use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
 use Magento\Framework\App\Config\Element;
-use Magento\Framework\App\ResourceConnection\Config;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\Exception\AlreadyExistsException;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Model\AbstractModel;
 use Magento\Framework\Model\ResourceModel\Db\ObjectRelationProcessor;
@@ -1108,6 +1109,7 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac
      * @param  \Magento\Framework\Model\AbstractModel $object
      * @return $this
      * @throws \Exception
+     * @throws AlreadyExistsException
      */
     public function save(\Magento\Framework\Model\AbstractModel $object)
     {
@@ -1147,6 +1149,10 @@ abstract class AbstractEntity extends AbstractResource implements EntityInterfac
             }
             $this->addCommitCallback([$object, 'afterCommitCallback'])->commit();
             $object->setHasDataChanges(false);
+        } catch (DuplicateException $e) {
+            $this->rollBack();
+            $object->setHasDataChanges(true);
+            throw new AlreadyExistsException(__('Unique constraint violation found'), $e);
         } catch (\Exception $e) {
             $this->rollBack();
             $object->setHasDataChanges(true);
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
index 4f13ee75bfe32c29ae544c19e950691ead763323..0d8d18df223767b2812f58356de3b7904090d8f7 100644
--- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php
@@ -6,8 +6,8 @@
 
 namespace Magento\Eav\Model\Entity\Attribute;
 
-use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Api\AttributeValueFactory;
+use Magento\Framework\Exception\LocalizedException;
 
 /**
  * Entity/Attribute/Model - attribute abstract
@@ -595,11 +595,10 @@ abstract class AbstractAttribute extends \Magento\Framework\Model\AbstractExtens
     {
         /** @var array $emptyStringTypes list of attribute types that treat empty string as a possible value */
         $emptyStringTypes = ['int', 'decimal', 'datetime', 'varchar', 'text', 'static'];
-        $attributeType = $this->getBackend()->getType();
         return (is_array($value) && count($value) == 0)
             || $value === null
-            || ($value === false && $attributeType != 'int')
-            || ($value === '' && in_array($attributeType, $emptyStringTypes));
+            || ($value === false && $this->getBackend()->getType() != 'int')
+            || ($value === '' && in_array($this->getBackend()->getType(), $emptyStringTypes));
     }
 
     /**
diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AttributeGroupAlreadyExistsException.php b/app/code/Magento/Eav/Model/Entity/Attribute/AttributeGroupAlreadyExistsException.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca80ca089a2ea4cc3b297b4dc933b53049ed31f1
--- /dev/null
+++ b/app/code/Magento/Eav/Model/Entity/Attribute/AttributeGroupAlreadyExistsException.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Eav\Model\Entity\Attribute;
+
+use Magento\Framework\Exception\AlreadyExistsException;
+
+/**
+ * Class AttributeGroupAlreadyExistsException
+ */
+class AttributeGroupAlreadyExistsException extends AlreadyExistsException
+{
+}
diff --git a/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php b/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..439f550a2bf020fadd8ae18abfb2348f4b18cf6b
--- /dev/null
+++ b/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Eav\Model\ResourceModel;
+
+use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository;
+use Magento\Eav\Model\Entity\AttributeCache;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\EntityManager\MetadataPool;
+
+/**
+ * Сlass responsible for loading and caching of attributes related to the given attribute set.
+ *
+ * Can be used to improve performance of services that mostly read attribute data.
+ */
+class AttributeLoader
+{
+    /** Name of ATTRIBUTE_SET_ID field */
+    const ATTRIBUTE_SET_ID = 'attribute_set_id';
+
+    /**
+     * @var AttributeRepository
+     */
+    private $attributeRepository;
+
+    /**
+     * @var MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * @var SearchCriteriaBuilder
+     */
+    private $searchCriteriaBuilder;
+
+    /**
+     * @var AttributeCache
+     */
+    private $attributeCache;
+
+    /**
+     * AttributeLoader constructor.
+     * @param AttributeRepository $attributeRepository
+     * @param MetadataPool $metadataPool
+     * @param SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param AttributeCache $attributeCache
+     */
+    public function __construct(
+        AttributeRepository $attributeRepository,
+        MetadataPool $metadataPool,
+        SearchCriteriaBuilder $searchCriteriaBuilder,
+        AttributeCache $attributeCache
+    ) {
+        $this->attributeRepository = $attributeRepository;
+        $this->metadataPool = $metadataPool;
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->attributeCache = $attributeCache;
+    }
+
+    /**
+     * Get attributes list from attribute set
+     *
+     * @param string $entityType
+     * @param int $attributeSetId
+     * @return \Magento\Eav\Api\Data\AttributeInterface[]|\object[]
+     */
+    public function getAttributes($entityType, $attributeSetId = null)
+    {
+        $suffix =  self::ATTRIBUTE_SET_ID . '-' . ($attributeSetId ?: 'all');
+        if ($attributes = $this->attributeCache->getAttributes($entityType, $suffix)) {
+            return $attributes;
+        }
+
+        $metadata = $this->metadataPool->getMetadata($entityType);
+
+        if ($attributeSetId === null) {
+            $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID, null, 'neq')->create();
+        } else {
+            $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID, $attributeSetId)->create();
+        }
+
+        $searchResult = $this->attributeRepository->getList(
+            $metadata->getEavEntityType(),
+            $criteria
+        );
+        $attributes = $searchResult->getItems();
+
+        $this->attributeCache->saveAttributes(
+            $entityType,
+            $attributes,
+            $suffix
+        );
+        return $attributes;
+    }
+}
diff --git a/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php b/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php
index 74373cb593c4086c06378050e1fa89b944d78be6..5e340595df9867b1cb02fd795c6fc9be7e34fc02 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php
@@ -113,13 +113,14 @@ class AttributePersistor
             return;
         }
         $metadata = $this->metadataPool->getMetadata($entityType);
+        $linkField = $metadata->getLinkField();
         foreach ($this->delete[$entityType] as $link => $data) {
             $attributeCodes = array_keys($data);
             foreach ($attributeCodes as $attributeCode) {
                 /** @var AbstractAttribute $attribute */
                 $attribute = $this->attributeRepository->get($metadata->getEavEntityType(), $attributeCode);
                 $conditions = [
-                    $metadata->getLinkField() . ' = ?' => $link,
+                    $linkField . ' = ?' => $link,
                     'attribute_id = ?' => $attribute->getAttributeId()
                 ];
                 foreach ($context as $scope) {
@@ -147,6 +148,7 @@ class AttributePersistor
             return;
         }
         $metadata = $this->metadataPool->getMetadata($entityType);
+        $linkField = $metadata->getLinkField();
         foreach ($this->insert[$entityType] as $link => $data) {
             foreach ($data as $attributeCode => $attributeValue) {
                 /** @var AbstractAttribute $attribute */
@@ -155,7 +157,7 @@ class AttributePersistor
                     $attributeCode
                 );
                 $data = [
-                    $metadata->getLinkField() => $link,
+                    $linkField => $link,
                     'attribute_id' => $attribute->getAttributeId(),
                     'value' => $this->prepareValue($entityType, $attributeValue, $attribute)
                 ];
@@ -180,6 +182,7 @@ class AttributePersistor
             return;
         }
         $metadata = $this->metadataPool->getMetadata($entityType);
+        $linkField = $metadata->getLinkField();
         foreach ($this->update[$entityType] as $link => $data) {
             foreach ($data as $attributeCode => $attributeValue) {
                 /** @var AbstractAttribute $attribute */
@@ -188,7 +191,7 @@ class AttributePersistor
                     $attributeCode
                 );
                 $conditions = [
-                    $metadata->getLinkField() . ' = ?' => $link,
+                    $linkField . ' = ?' => $link,
                     'attribute_id = ?' => $attribute->getAttributeId(),
                 ];
                 foreach ($context as $scope) {
diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php
index 1a8aaaf027f561966101e360020fb3a2441e27d6..df411b6a21698033c60839c0d855c3b0ed4e96e5 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php
@@ -6,9 +6,10 @@
 namespace Magento\Eav\Model\ResourceModel;
 
 use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository;
-use Magento\Framework\EntityManager\Operation\AttributeInterface;
-use Magento\Framework\EntityManager\MetadataPool;
 use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\EntityManager\MetadataPool;
+use Magento\Framework\EntityManager\Operation\AttributeInterface;
 use Magento\Framework\Model\Entity\ScopeResolver;
 
 /**
@@ -42,40 +43,43 @@ class CreateHandler implements AttributeInterface
      */
     private $scopeResolver;
 
+    /**
+     * @var AttributeLoader
+     */
+    private $attributeLoader;
+
     /**
      * @param AttributeRepository $attributeRepository
      * @param MetadataPool $metadataPool
      * @param SearchCriteriaBuilder $searchCriteriaBuilder
      * @param AttributePersistor $attributePersistor
      * @param ScopeResolver $scopeResolver
+     * @param AttributeLoader $attributeLoader
      */
     public function __construct(
         AttributeRepository $attributeRepository,
         MetadataPool $metadataPool,
         SearchCriteriaBuilder $searchCriteriaBuilder,
         AttributePersistor $attributePersistor,
-        ScopeResolver $scopeResolver
+        ScopeResolver $scopeResolver,
+        AttributeLoader $attributeLoader = null
     ) {
         $this->attributeRepository = $attributeRepository;
         $this->metadataPool = $metadataPool;
         $this->searchCriteriaBuilder = $searchCriteriaBuilder;
         $this->attributePersistor = $attributePersistor;
         $this->scopeResolver = $scopeResolver;
+        $this->attributeLoader = $attributeLoader ?: ObjectManager::getInstance()->get(AttributeLoader::class);
     }
 
     /**
      * @param string $entityType
+     * @param int $attributeSetId
      * @return \Magento\Eav\Api\Data\AttributeInterface[]
-     * @throws \Exception
      */
-    protected function getAttributes($entityType)
+    protected function getAttributes($entityType, $attributeSetId = null)
     {
-        $metadata = $this->metadataPool->getMetadata($entityType);
-        $searchResult = $this->attributeRepository->getList(
-            $metadata->getEavEntityType(),
-            $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create()
-        );
-        return $searchResult->getItems();
+        return $this->attributeLoader->getAttributes($entityType, $attributeSetId);
     }
 
     /**
@@ -92,23 +96,28 @@ class CreateHandler implements AttributeInterface
         $metadata = $this->metadataPool->getMetadata($entityType);
         if ($metadata->getEavEntityType()) {
             $processed = [];
+            $entityLinkField = $metadata->getLinkField();
+            $attributeSetId = isset($entityData[AttributeLoader::ATTRIBUTE_SET_ID])
+                ? $entityData[AttributeLoader::ATTRIBUTE_SET_ID]
+                : null; // @todo verify is it normal to not have attributer_set_id
             /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */
-            foreach ($this->getAttributes($entityType) as $attribute) {
+            foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) {
                 if ($attribute->isStatic()) {
                     continue;
                 }
-                if (isset($entityData[$attribute->getAttributeCode()])
-                    && !is_array($entityData[$attribute->getAttributeCode()])
-                    && !$attribute->isValueEmpty($entityData[$attribute->getAttributeCode()])
+
+                $attributeCode = $attribute->getAttributeCode();
+                if (isset($entityData[$attributeCode])
+                    && !is_array($entityData[$attributeCode])
+                    && !$attribute->isValueEmpty($entityData[$attributeCode])
                 ) {
-                    $entityLinkField = $metadata->getLinkField();
                     $this->attributePersistor->registerInsert(
                         $entityType,
                         $entityData[$entityLinkField],
-                        $attribute->getAttributeCode(),
-                        $entityData[$attribute->getAttributeCode()]
+                        $attributeCode,
+                        $entityData[$attributeCode]
                     );
-                    $processed[$attribute->getAttributeCode()] = $entityData[$attribute->getAttributeCode()];
+                    $processed[$attributeCode] = $entityData[$attributeCode];
                 }
             }
             $context = $this->scopeResolver->getEntityContext($entityType, $entityData);
diff --git a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Group.php b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Group.php
index 796f4d19f2eb8d90c4625542a33257e3a065a5af..9515bbc66437859d984f126b4d49f483c72f8194 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Group.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute/Group.php
@@ -5,6 +5,10 @@
  */
 namespace Magento\Eav\Model\ResourceModel\Entity\Attribute;
 
+use Magento\Eav\Model\Entity\Attribute\AttributeGroupAlreadyExistsException;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\Model\AbstractModel;
+
 /**
  * Eav Resource Entity Attribute Group
  *
@@ -50,10 +54,10 @@ class Group extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     /**
      * Perform actions before object save
      *
-     * @param \Magento\Framework\Model\AbstractModel $object
+     * @param AbstractModel $object
      * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      */
-    protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
+    protected function _beforeSave(AbstractModel $object)
     {
         if (!$object->getSortOrder()) {
             $object->setSortOrder($this->_getMaxSortOrder($object) + 1);
@@ -64,10 +68,10 @@ class Group extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     /**
      * Perform actions after object save
      *
-     * @param \Magento\Framework\Model\AbstractModel $object
+     * @param AbstractModel $object
      * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
      */
-    protected function _afterSave(\Magento\Framework\Model\AbstractModel $object)
+    protected function _afterSave(AbstractModel $object)
     {
         if ($object->getAttributes()) {
             foreach ($object->getAttributes() as $attribute) {
@@ -82,7 +86,7 @@ class Group extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
     /**
      * Retrieve max sort order
      *
-     * @param \Magento\Framework\Model\AbstractModel $object
+     * @param AbstractModel $object
      * @return int
      */
     protected function _getMaxSortOrder($object)
@@ -130,4 +134,38 @@ class Group extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
 
         return $this;
     }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function saveNewObject(AbstractModel $object)
+    {
+        try {
+            return parent::saveNewObject($object);
+        } catch (DuplicateException $e) {
+            throw new AttributeGroupAlreadyExistsException(
+                __(
+                    'Attribute group with same code already exist. Please rename "%1" group',
+                    $object->getAttributeGroupName()
+                )
+            );
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function updateObject(AbstractModel $object)
+    {
+        try {
+            return parent::updateObject($object);
+        } catch (DuplicateException $e) {
+            throw new AttributeGroupAlreadyExistsException(
+                __(
+                    'Attribute group with same code already exist. Please rename "%1" group',
+                    $object->getAttributeGroupName()
+                )
+            );
+        }
+    }
 }
diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
index 0f892a272fa1a209ca7b2e632abc98db5afb5d3d..c775a24a03c465dad45640a7f634823cb8b43ef8 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php
@@ -54,6 +54,11 @@ class UpdateHandler implements AttributeInterface
      */
     private $readHandler;
 
+    /**
+     * @var AttributeLoader
+     */
+    private $attributeLoader;
+
     /**
      * UpdateHandler constructor.
      * @param AttributeRepository $attributeRepository
@@ -62,6 +67,7 @@ class UpdateHandler implements AttributeInterface
      * @param AttributePersistor $attributePersistor
      * @param ReadSnapshot $readSnapshot
      * @param ScopeResolver $scopeResolver
+     * @param AttributeLoader $attributeLoader
      */
     public function __construct(
         AttributeRepository $attributeRepository,
@@ -69,7 +75,8 @@ class UpdateHandler implements AttributeInterface
         SearchCriteriaBuilder $searchCriteriaBuilder,
         AttributePersistor $attributePersistor,
         ReadSnapshot $readSnapshot,
-        ScopeResolver $scopeResolver
+        ScopeResolver $scopeResolver,
+        AttributeLoader $attributeLoader = null
     ) {
         $this->attributeRepository = $attributeRepository;
         $this->metadataPool = $metadataPool;
@@ -77,22 +84,17 @@ class UpdateHandler implements AttributeInterface
         $this->attributePersistor = $attributePersistor;
         $this->readSnapshot = $readSnapshot;
         $this->scopeResolver = $scopeResolver;
+        $this->attributeLoader = $attributeLoader ?: ObjectManager::getInstance()->get(AttributeLoader::class);
     }
 
     /**
      * @param string $entityType
+     * @param int $attributeSetId
      * @return \Magento\Eav\Api\Data\AttributeInterface[]
-     * @throws \Exception
      */
-    protected function getAttributes($entityType)
+    protected function getAttributes($entityType, $attributeSetId = null)
     {
-        $metadata = $this->metadataPool->getMetadata($entityType);
-
-        $searchResult = $this->attributeRepository->getList(
-            $metadata->getEavEntityType(),
-            $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create()
-        );
-        return $searchResult->getItems();
+        return $this->attributeLoader->getAttributes($entityType, $attributeSetId);
     }
 
     /**
@@ -118,8 +120,11 @@ class UpdateHandler implements AttributeInterface
                     $entityDataForSnapshot[$scope->getIdentifier()] = $entityData[$scope->getIdentifier()];
                 }
             }
+            $attributeSetId = isset($entityData[AttributeLoader::ATTRIBUTE_SET_ID])
+                ? $entityData[AttributeLoader::ATTRIBUTE_SET_ID]
+                : null; // @todo verify is it normal to not have attributer_set_id
             $snapshot = $this->readSnapshot->execute($entityType, $entityDataForSnapshot);
-            foreach ($this->getAttributes($entityType) as $attribute) {
+            foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) {
                 if ($attribute->isStatic()) {
                     continue;
                 }
diff --git a/app/code/Magento/Eav/Setup/UpgradeSchema.php b/app/code/Magento/Eav/Setup/UpgradeSchema.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b52bba7c2efbd379851ddf16a0cafbc077dcaf2
--- /dev/null
+++ b/app/code/Magento/Eav/Setup/UpgradeSchema.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Eav\Setup;
+
+use Magento\Framework\Setup\UpgradeSchemaInterface;
+use Magento\Framework\Setup\ModuleContextInterface;
+use Magento\Framework\Setup\SchemaSetupInterface;
+
+/**
+ * Upgrade the Eav module DB scheme
+ */
+class UpgradeSchema implements UpgradeSchemaInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context)
+    {
+        $setup->startSetup();
+
+        if (version_compare($context->getVersion(), '2.1.0', '<')) {
+            $this->addUniqueKeyToEavAttributeGroupTable($setup);
+        }
+        $setup->endSetup();
+    }
+
+    /**
+     * @param SchemaSetupInterface $setup
+     * @return void
+     */
+    private function addUniqueKeyToEavAttributeGroupTable(SchemaSetupInterface $setup)
+    {
+        $setup->getConnection()->addIndex(
+            $setup->getTable('eav_attribute_group'),
+            $setup->getIdxName(
+                'catalog_category_product',
+                ['attribute_set_id', 'attribute_group_code'],
+                \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE
+            ),
+            ['attribute_set_id', 'attribute_group_code'],
+            \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE
+        );
+    }
+}
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/AbstractEntityTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/AbstractEntityTest.php
index f39eeb892757645df9fc3157894249e18d8c4803..7628207e25a52c2ea7cd595bd47f65fb0d18ef81 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Entity/AbstractEntityTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/AbstractEntityTest.php
@@ -5,13 +5,17 @@
  */
 namespace Magento\Eav\Test\Unit\Model\Entity;
 
+use Magento\Eav\Model\Entity\AbstractEntity;
+use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\Model\AbstractModel;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class AbstractEntityTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * Entity model to be tested
-     * @var \Magento\Eav\Model\Entity\AbstractEntity|\PHPUnit_Framework_MockObject_MockObject
+     * @var AbstractEntity|\PHPUnit_Framework_MockObject_MockObject
      */
     protected $_model;
 
@@ -23,11 +27,11 @@ class AbstractEntityTest extends \PHPUnit_Framework_TestCase
         $objectManager = new ObjectManager($this);
         $this->eavConfig = $this->getMock(\Magento\Eav\Model\Config::class, [], [], '', false);
         $arguments =  $objectManager->getConstructArguments(
-            \Magento\Eav\Model\Entity\AbstractEntity::class,
+            AbstractEntity::class,
             ['eavConfig' => $this->eavConfig]
         );
         $this->_model = $this->getMockForAbstractClass(
-            \Magento\Eav\Model\Entity\AbstractEntity::class,
+            AbstractEntity::class,
             $arguments
         );
     }
@@ -113,7 +117,7 @@ class AbstractEntityTest extends \PHPUnit_Framework_TestCase
     /**
      * Get adapter mock
      *
-     * @return \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\DB\Adapter\AdapterInterface
+     * @return \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\DB\Adapter\Pdo\Mysql
      */
     protected function _getConnectionMock()
     {
@@ -300,7 +304,7 @@ class AbstractEntityTest extends \PHPUnit_Framework_TestCase
         $objectManager = new ObjectManager($this);
         $this->eavConfig = $this->getMock(\Magento\Eav\Model\Config::class, [], [], '', false);
         $arguments =  $objectManager->getConstructArguments(
-            \Magento\Eav\Model\Entity\AbstractEntity::class,
+            AbstractEntity::class,
             [
                 'eavConfig' => $eavConfig,
                 'data' => [
@@ -310,8 +314,8 @@ class AbstractEntityTest extends \PHPUnit_Framework_TestCase
                 ]
             ]
         );
-        /** @var $model \Magento\Framework\Model\AbstractModel|\PHPUnit_Framework_MockObject_MockObject */
-        $model = $this->getMockBuilder(\Magento\Eav\Model\Entity\AbstractEntity::class)
+        /** @var $model AbstractEntity|\PHPUnit_Framework_MockObject_MockObject */
+        $model = $this->getMockBuilder(AbstractEntity::class)
             ->setConstructorArgs($arguments)
             ->setMethods(['_getValue', 'beginTransaction', 'commit', 'rollback', 'getConnection'])
             ->getMock();
@@ -353,4 +357,30 @@ class AbstractEntityTest extends \PHPUnit_Framework_TestCase
             ]
         ];
     }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\AlreadyExistsException
+     */
+    public function testDuplicateExceptionProcessingOnSave()
+    {
+        $connection = $this->getMock(AdapterInterface::class);
+        $connection->expects($this->once())->method('rollback');
+
+        /** @var AbstractEntity|\PHPUnit_Framework_MockObject_MockObject $model */
+        $model = $this->getMockBuilder(AbstractEntity::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getConnection'])
+            ->getMockForAbstractClass();
+        $model->expects($this->any())->method('getConnection')->willReturn($connection);
+
+        /** @var AbstractModel|\PHPUnit_Framework_MockObject_MockObject $object */
+        $object = $this->getMockBuilder(AbstractModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $object->expects($this->once())->method('hasDataChanges')->willReturn(true);
+        $object->expects($this->once())->method('beforeSave')->willThrowException(new DuplicateException());
+        $object->expects($this->once())->method('setHasDataChanges')->with(true);
+
+        $model->save($object);
+    }
 }
diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php
index d6b88e0ac56915c3b116ac740b18cfd31ae9f876..a10bafacda42dc2b72fcc285307a0250cae3074b 100644
--- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php
+++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php
@@ -207,7 +207,7 @@ class AbstractAttributeTest extends \PHPUnit_Framework_TestCase
             ]
         );
         $backendModelMock->expects($this->any())->method('getType')->willReturn($attributeType);
-        $model->expects($this->once())->method('getBackend')->willReturn($backendModelMock);
+        $model->expects($this->any())->method('getBackend')->willReturn($backendModelMock);
         $this->assertEquals($isEmpty, $model->isValueEmpty($value));
     }
 
diff --git a/app/code/Magento/Eav/etc/module.xml b/app/code/Magento/Eav/etc/module.xml
index c1c313d91501989578f03b404ae91acc2d0ccbb1..d03606d1eeb900a1dfec428c172cba0b47a01fc0 100644
--- a/app/code/Magento/Eav/etc/module.xml
+++ b/app/code/Magento/Eav/etc/module.xml
@@ -6,7 +6,7 @@
  */
 -->
 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
-    <module name="Magento_Eav" setup_version="2.0.0">
+    <module name="Magento_Eav" setup_version="2.1.0">
         <sequence>
             <module name="Magento_Store"/>
         </sequence>
diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php
index 5eeb7ddb61fa05657b08dda4711e82938e8778ce..0ad53eeeb90d9dab3a8877624429efefe314aefd 100644
--- a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php
+++ b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php
@@ -51,6 +51,7 @@ class ApiDataFixture
      */
     public function startTest(\PHPUnit_Framework_TestCase $test)
     {
+        \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize();
         /** Apply method level fixtures if thy are available, apply class level fixtures otherwise */
         $this->_applyFixtures($this->_getFixtures('method', $test) ?: $this->_getFixtures('class', $test));
     }
@@ -61,6 +62,9 @@ class ApiDataFixture
     public function endTest()
     {
         $this->_revertFixtures();
+        /** @var $objectManager \Magento\TestFramework\ObjectManager */
+        $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+        $objectManager->get(\Magento\Eav\Model\Entity\AttributeCache::class)->clear();
     }
 
     /**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4738e1886be3e6d9ab64f65cd7f241b63453a741
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Set/SaveTest.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Controller\Adminhtml\Product\Set;
+
+use Magento\Eav\Api\AttributeSetRepositoryInterface;
+use Magento\Eav\Api\Data\AttributeSetInterface;
+use Magento\Framework\Api\SearchCriteriaBuilder;
+use Magento\TestFramework\Helper\Bootstrap;
+
+class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController
+{
+    /**
+     * @magentoDataFixture Magento/Catalog/_files/attribute_set_with_renamed_group.php
+     */
+    public function testAlreadyExistsExceptionProcessingWhenGroupCodeIsDuplicated()
+    {
+        $attributeSet = $this->getAttributeSetByName('attribute_set_test');
+        $this->assertNotEmpty($attributeSet, 'Attribute set with name "attribute_set_test" is missed');
+
+        $this->getRequest()->setPostValue('data', json_encode([
+            'attribute_set_name' => 'attribute_set_test',
+            'groups' => [
+                ['ynode-418', 'attribute-group-name', 1],
+            ],
+            'attributes' => [
+                ['9999', 'ynode-418', 1, null]
+            ],
+            'not_attributes' => [],
+            'removeGroups' => [],
+        ]));
+        $this->dispatch('backend/catalog/product_set/save/id/' . $attributeSet->getAttributeSetId());
+
+        $jsonResponse = json_decode($this->getResponse()->getBody());
+        $this->assertNotNull($jsonResponse);
+        $this->assertEquals(1, $jsonResponse->error);
+        $this->assertContains(
+            'Attribute group with same code already exist. Please rename &quot;attribute-group-name&quot; group',
+            $jsonResponse->message
+        );
+    }
+
+    /**
+     * @param string $attributeSetName
+     * @return AttributeSetInterface|null
+     */
+    protected function getAttributeSetByName($attributeSetName)
+    {
+        $objectManager = Bootstrap::getObjectManager();
+
+        /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
+        $searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class);
+        $searchCriteriaBuilder->addFilter('attribute_set_name', $attributeSetName);
+
+        /** @var AttributeSetRepositoryInterface $attributeSetRepository */
+        $attributeSetRepository = $objectManager->get(AttributeSetRepositoryInterface::class);
+        $result = $attributeSetRepository->getList($searchCriteriaBuilder->create());
+
+        $items = $result->getItems();
+        return $result->getTotalCount() ? array_pop($items) : null;
+    }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_renamed_group.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_renamed_group.php
new file mode 100644
index 0000000000000000000000000000000000000000..d73baa9e70e023d55e0a3306a03f8b7a56666b86
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/attribute_set_with_renamed_group.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+use Magento\Catalog\Api\AttributeSetRepositoryInterface;
+use Magento\Eav\Api\AttributeGroupRepositoryInterface;
+use Magento\Eav\Api\Data\AttributeGroupInterface;
+use Magento\Eav\Api\Data\AttributeGroupInterfaceFactory;
+use Magento\Eav\Api\Data\AttributeSetInterface;
+use Magento\Eav\Api\Data\AttributeSetInterfaceFactory;
+use Magento\Eav\Model\Entity\Type;
+use Magento\Framework\Api\DataObjectHelper;
+use Magento\TestFramework\Helper\Bootstrap;
+
+$objectManager = Bootstrap::getObjectManager();
+$attributeSetFactory = $objectManager->get(AttributeSetInterfaceFactory::class);
+$attributeGroupFactory = $objectManager->get(AttributeGroupInterfaceFactory::class);
+/** @var DataObjectHelper $dataObjectHelper */
+$dataObjectHelper = $objectManager->get(DataObjectHelper::class);
+/** @var AttributeGroupRepositoryInterface $attributeGroupRepository */
+$attributeGroupRepository = $objectManager->get(AttributeGroupRepositoryInterface::class);
+/** @var AttributeSetRepositoryInterface $attributeSetRepository */
+$attributeSetRepository = $objectManager->get(AttributeSetRepositoryInterface::class);
+
+/** @var AttributeSetInterface $attributeSet */
+$attributeSet = $attributeSetFactory->create();
+$entityTypeId = $objectManager->create(Type::class)->loadByCode('catalog_product')->getId();
+$dataObjectHelper->populateWithArray(
+    $attributeSet,
+    [
+        'attribute_set_name' => 'attribute_set_test',
+        'entity_type_id' => $entityTypeId,
+    ],
+    AttributeSetInterface::class
+);
+$attributeSetRepository->save($attributeSet);
+
+/** @var AttributeGroupInterface $attributeGroup */
+$attributeGroup = $attributeGroupFactory->create();
+$dataObjectHelper->populateWithArray(
+    $attributeGroup,
+    [
+        'attribute_set_id' => $attributeSet->getAttributeSetId(),
+        'attribute_group_name' => 'attribute-group-name',
+        'default_id' => 1,
+    ],
+    AttributeGroupInterface::class
+);
+$attributeGroupRepository->save($attributeGroup);
+
+// during renaming group code is not changed
+$attributeGroup->setAttributeGroupName('attribute-group-renamed');
+$attributeGroupRepository->save($attributeGroup);
diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php
index 7fb35a4412bf8bbbda969b646a23eee84aac61ac..ec230f3c5126185e21d27226c65f6e09e7f125e5 100644
--- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php
+++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php
@@ -56,7 +56,7 @@ $product->setTypeId(
 )->setId(
     444
 )->setAttributeSetId(
-    5
+    4
 )->setStoreId(
     1
 )->setWebsiteIds(
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt
index 9ae1f7fc1452d3156aa7308ee3f81d194e99bbce..a50845d3884adf110ff8c36831e6ef8ac63670ca 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/exception_hierarchy.txt
@@ -6,3 +6,4 @@
 \Magento\Framework\DB\Adapter\ConnectionException
 \Magento\Framework\DB\Adapter\DeadlockException
 \Magento\Framework\DB\Adapter\LockWaitException
+\Magento\Framework\DB\Adapter\DuplicateException
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php
index f055f0a732c664dd8ed109d7ec52b178c537a4d0..d120a4543b9ddced597d330ab489de15c80cdb86 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/security/blacklist.php
@@ -4,5 +4,6 @@
  * See COPYING.txt for license details.
  */
 return [
-    '/Test\/Unit/'
+    '/Test\/Unit/',
+    '/lib\/internal\/Magento\/Framework\/DB\/Adapter\/Pdo\/Mysql\.php/',
 ];
diff --git a/lib/internal/Magento/Framework/DB/Adapter/DuplicateException.php b/lib/internal/Magento/Framework/DB/Adapter/DuplicateException.php
new file mode 100644
index 0000000000000000000000000000000000000000..06c4dfcc30ab26572b0faae1d90d1939989455ba
--- /dev/null
+++ b/lib/internal/Magento/Framework/DB/Adapter/DuplicateException.php
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\DB\Adapter;
+
+/**
+ * Database duplicate exception
+ */
+class DuplicateException extends \Zend_Db_Adapter_Exception
+{
+}
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
index 6399824590a696980b641abcd3d0fc5b4e09cf42..cfbeb4f54c9e29d2f7b320b57c51ae7a7b31a676 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
@@ -14,6 +14,7 @@ use Magento\Framework\Cache\FrontendInterface;
 use Magento\Framework\DB\Adapter\AdapterInterface;
 use Magento\Framework\DB\Adapter\ConnectionException;
 use Magento\Framework\DB\Adapter\DeadlockException;
+use Magento\Framework\DB\Adapter\DuplicateException;
 use Magento\Framework\DB\Adapter\LockWaitException;
 use Magento\Framework\DB\Ddl\Table;
 use Magento\Framework\DB\ExpressionConverter;
@@ -232,6 +233,8 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
             1205 => LockWaitException::class,
             // SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock
             1213 => DeadlockException::class,
+            // SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
+            1062 => DuplicateException::class,
         ];
         try {
             parent::__construct($config);
@@ -465,7 +468,7 @@ class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
      * @param mixed $bind An array of data or data itself to bind to the placeholders.
      * @return \Zend_Db_Statement_Pdo|void
      * @throws \Zend_Db_Adapter_Exception To re-throw \PDOException.
-     * @throws LocalizedException In case multiple queries are attempted at once, to protect from SQL injection
+     * @throws \Zend_Db_Statement_Exception
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     protected function _query($sql, $bind = [])
diff --git a/lib/internal/Magento/Framework/EntityManager/Operation/Create.php b/lib/internal/Magento/Framework/EntityManager/Operation/Create.php
index 7e3dfb2e753caa4ebd8efc72c159b0b3a3d521d4..552fe7bca86f58fb722fab1b08294b4ee41d3734 100644
--- a/lib/internal/Magento/Framework/EntityManager/Operation/Create.php
+++ b/lib/internal/Magento/Framework/EntityManager/Operation/Create.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Framework\EntityManager\Operation;
 
-use Magento\Framework\EntityManager\Operation\CreateInterface;
+use Magento\Framework\DB\Adapter\DuplicateException;
 use Magento\Framework\EntityManager\Operation\Create\CreateMain;
 use Magento\Framework\EntityManager\Operation\Create\CreateAttributes;
 use Magento\Framework\EntityManager\Operation\Create\CreateExtensions;
@@ -13,6 +13,8 @@ use Magento\Framework\EntityManager\MetadataPool;
 use Magento\Framework\EntityManager\EventManager;
 use Magento\Framework\EntityManager\TypeResolver;
 use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Exception\AlreadyExistsException;
+use Magento\Framework\Phrase;
 
 /**
  * Class Create
@@ -86,6 +88,7 @@ class Create implements CreateInterface
      * @param array $arguments
      * @return object
      * @throws \Exception
+     * @throws AlreadyExistsException
      */
     public function execute($entity, $arguments = [])
     {
@@ -114,6 +117,9 @@ class Create implements CreateInterface
                 ]
             );
             $connection->commit();
+        } catch (DuplicateException $e) {
+            $connection->rollBack();
+            throw new AlreadyExistsException(new Phrase('Unique constraint violation found'), $e);
         } catch (\Exception $e) {
             $connection->rollBack();
             throw $e;
diff --git a/lib/internal/Magento/Framework/EntityManager/Operation/Update.php b/lib/internal/Magento/Framework/EntityManager/Operation/Update.php
index 22abb464f8cd9ad96713899ca95fc0a3dc87e782..dbc8dc63873498888a40396795f9d6a0b9edca19 100644
--- a/lib/internal/Magento/Framework/EntityManager/Operation/Update.php
+++ b/lib/internal/Magento/Framework/EntityManager/Operation/Update.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Framework\EntityManager\Operation;
 
-use Magento\Framework\EntityManager\Operation\UpdateInterface;
+use Magento\Framework\DB\Adapter\DuplicateException;
 use Magento\Framework\EntityManager\Operation\Update\UpdateMain;
 use Magento\Framework\EntityManager\Operation\Update\UpdateAttributes;
 use Magento\Framework\EntityManager\Operation\Update\UpdateExtensions;
@@ -13,6 +13,8 @@ use Magento\Framework\EntityManager\MetadataPool;
 use Magento\Framework\EntityManager\EventManager;
 use Magento\Framework\EntityManager\TypeResolver;
 use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\Exception\AlreadyExistsException;
+use Magento\Framework\Phrase;
 
 /**
  * Class Update
@@ -114,6 +116,9 @@ class Update implements UpdateInterface
                 ]
             );
             $connection->commit();
+        } catch (DuplicateException $e) {
+            $connection->rollBack();
+            throw new AlreadyExistsException(new Phrase('Unique constraint violation found'), $e);
         } catch (\Exception $e) {
             $connection->rollBack();
             throw $e;
diff --git a/lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/CreateTest.php b/lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/CreateTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e45e8ffac1934859b16fd040505865d2f078eed3
--- /dev/null
+++ b/lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/CreateTest.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\EntityManager\Test\Unit\Operation;
+
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\DataObject;
+use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\EntityManager\EntityMetadataInterface;
+use Magento\Framework\EntityManager\MetadataPool;
+use Magento\Framework\EntityManager\Operation\Create;
+use Magento\Framework\EntityManager\Operation\Create\CreateMain;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class CreateTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $metadataPool;
+
+    /**
+     * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resourceConnection;
+
+    /**
+     * @var CreateMain|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $createMain;
+
+    /**
+     * @var Create
+     */
+    private $create;
+
+    public function setUp()
+    {
+        $this->metadataPool = $this->getMockBuilder(MetadataPool::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->resourceConnection = $this->getMockBuilder(ResourceConnection::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->createMain = $this->getMockBuilder(CreateMain::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->create = (new ObjectManager($this))->getObject(Create::class, [
+            'metadataPool' => $this->metadataPool,
+            'resourceConnection' => $this->resourceConnection,
+            'createMain' => $this->createMain,
+        ]);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\AlreadyExistsException
+     */
+    public function testDuplicateExceptionProcessingOnExecute()
+    {
+        $metadata = $this->getMock(EntityMetadataInterface::class);
+        $this->metadataPool->expects($this->any())->method('getMetadata')->willReturn($metadata);
+
+        $connection = $this->getMock(AdapterInterface::class);
+        $connection->expects($this->once())->method('rollback');
+        $this->resourceConnection->expects($this->any())->method('getConnectionByName')->willReturn($connection);
+
+        $this->createMain->expects($this->once())->method('execute')->willThrowException(new DuplicateException());
+
+        $entity = $this->getMockBuilder(DataObject::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->create->execute($entity);
+    }
+}
diff --git a/lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/UpdateTest.php b/lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/UpdateTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ec78c2560a28b3d0cd2043b8fa09fe653e49be8
--- /dev/null
+++ b/lib/internal/Magento/Framework/EntityManager/Test/Unit/Operation/UpdateTest.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\EntityManager\Test\Unit\Operation;
+
+use Magento\Framework\App\ResourceConnection;
+use Magento\Framework\DataObject;
+use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\EntityManager\EntityMetadataInterface;
+use Magento\Framework\EntityManager\MetadataPool;
+use Magento\Framework\EntityManager\Operation\Update;
+use Magento\Framework\EntityManager\Operation\Update\UpdateMain;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class UpdateTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $metadataPool;
+
+    /**
+     * @var ResourceConnection|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resourceConnection;
+
+    /**
+     * @var UpdateMain|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $updateMain;
+
+    /**
+     * @var Update
+     */
+    private $update;
+
+    public function setUp()
+    {
+        $this->metadataPool = $this->getMockBuilder(MetadataPool::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->resourceConnection = $this->getMockBuilder(ResourceConnection::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->updateMain = $this->getMockBuilder(UpdateMain::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->update = (new ObjectManager($this))->getObject(Update::class, [
+            'metadataPool' => $this->metadataPool,
+            'resourceConnection' => $this->resourceConnection,
+            'updateMain' => $this->updateMain,
+        ]);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\AlreadyExistsException
+     */
+    public function testDuplicateExceptionProcessingOnExecute()
+    {
+        $metadata = $this->getMock(EntityMetadataInterface::class);
+        $this->metadataPool->expects($this->any())->method('getMetadata')->willReturn($metadata);
+
+        $connection = $this->getMock(AdapterInterface::class);
+        $connection->expects($this->once())->method('rollback');
+        $this->resourceConnection->expects($this->any())->method('getConnectionByName')->willReturn($connection);
+
+        $this->updateMain->expects($this->once())->method('execute')->willThrowException(new DuplicateException());
+
+        $entity = $this->getMockBuilder(DataObject::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->update->execute($entity);
+    }
+}
diff --git a/lib/internal/Magento/Framework/Exception/AlreadyExistsException.php b/lib/internal/Magento/Framework/Exception/AlreadyExistsException.php
index dd39ec8c709a75fa4c4a8721dd8fdbfed4499c2f..18afdacf53de2676525f70fd76e5b94b3671dddb 100644
--- a/lib/internal/Magento/Framework/Exception/AlreadyExistsException.php
+++ b/lib/internal/Magento/Framework/Exception/AlreadyExistsException.php
@@ -3,9 +3,24 @@
  * Copyright © 2016 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-
 namespace Magento\Framework\Exception;
 
+use Magento\Framework\Phrase;
+
+/**
+ * Class AlreadyExistsException
+ */
 class AlreadyExistsException extends LocalizedException
 {
+    /**
+     * @param Phrase $phrase
+     * @param \Exception $cause
+     */
+    public function __construct(Phrase $phrase = null, \Exception $cause = null)
+    {
+        if ($phrase === null) {
+            $phrase = new Phrase('Unique constraint violation found');
+        }
+        parent::__construct($phrase, $cause);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
index 7febcb450f9720d9999f19704808df90ca69deea..4cd88e356e9cad79a6f0209bb2bab685ffe5f73c 100644
--- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php
@@ -10,6 +10,8 @@ use Magento\Framework\App\ResourceConnection;
 use Magento\Framework\Exception\AlreadyExistsException;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Model\ResourceModel\AbstractResource;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\Phrase;
 
 /**
  * Abstract resource model class
@@ -379,6 +381,7 @@ abstract class AbstractDb extends AbstractResource
      * @return $this
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      * @throws \Exception
+     * @throws AlreadyExistsException
      * @api
      */
     public function save(\Magento\Framework\Model\AbstractModel $object)
@@ -413,6 +416,10 @@ abstract class AbstractDb extends AbstractResource
             }
             $this->addCommitCallback([$object, 'afterCommitCallback'])->commit();
             $object->setHasDataChanges(false);
+        } catch (DuplicateException $e) {
+            $this->rollBack();
+            $object->setHasDataChanges(true);
+            throw new AlreadyExistsException(new Phrase('Unique constraint violation found'), $e);
         } catch (\Exception $e) {
             $this->rollBack();
             $object->setHasDataChanges(true);
diff --git a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
index 0a2e6f2262173f244dc0dddf8c53fa44c9bf3558..d13bcdc539aa8c82d7af3b24ea1d32ed93e8431b 100644
--- a/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
+++ b/lib/internal/Magento/Framework/Model/Test/Unit/ResourceModel/Db/AbstractDbTest.php
@@ -7,6 +7,10 @@
 // @codingStandardsIgnoreFile
 
 namespace Magento\Framework\Model\Test\Unit\ResourceModel\Db;
+use Magento\Framework\DB\Adapter\AdapterInterface;
+use Magento\Framework\DB\Adapter\DuplicateException;
+use Magento\Framework\Model\AbstractModel;
+use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
 
 /**
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -14,7 +18,7 @@ namespace Magento\Framework\Model\Test\Unit\ResourceModel\Db;
 class AbstractDbTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb
+     * @var AbstractDb
      */
     protected $_model;
 
@@ -63,7 +67,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             ->willReturn($this->transactionManagerMock);
 
         $this->_model = $this->getMockForAbstractClass(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             [$contextMock],
             '',
             true,
@@ -116,7 +120,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
     public function testAddUniqueFieldArray()
     {
         $this->assertInstanceOf(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             $this->_model->addUniqueField(['someField'])
         );
     }
@@ -134,7 +138,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
     {
         $data = 'MainTableName';
         $idFieldNameProperty = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, '_idFieldName'
+            AbstractDb::class, '_idFieldName'
         );
         $idFieldNameProperty->setAccessible(true);
         $idFieldNameProperty->setValue($this->_model, $data);
@@ -158,7 +162,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
     public function testGetMainTable($tableName, $expectedResult)
     {
         $mainTableProperty = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_mainTable'
         );
         $mainTableProperty->setAccessible(true);
@@ -195,7 +199,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             $this->returnValue('tableName')
         );
         $tablesProperty = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_tables'
         );
         $tablesProperty->setAccessible(true);
@@ -215,7 +219,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetChecksum($checksum, $expected)
     {
-        $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\AdapterInterface::class, [], [], '', false);
+        $connectionMock = $this->getMock(AdapterInterface::class, [], [], '', false);
         $connectionMock->expects($this->once())->method('getTablesChecksum')->with($checksum)->will(
             $this->returnValue([$checksum => 'checksum'])
         );
@@ -242,7 +246,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
     public function testResetUniqueField()
     {
         $uniqueFields = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_uniqueFields'
         );
         $uniqueFields->setAccessible(true);
@@ -254,7 +258,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
     public function testGetUniqueFields()
     {
         $uniqueFieldsReflection = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_uniqueFields'
         );
         $uniqueFieldsReflection->setAccessible(true);
@@ -281,14 +285,14 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($this->_model, $result);
         $this->assertInstanceOf(
             \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
-        $result
+            $result
         );
     }
 
     public function testDelete()
     {
         $connectionInterfaceMock = $this->getMock(
-            \Magento\Framework\DB\Adapter\AdapterInterface::class,
+            AdapterInterface::class,
             [],
             [],
             '',
@@ -297,7 +301,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false);
         $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false);
         $abstractModelMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Model\AbstractModel::class,
+            AbstractModel::class,
             [$contextMock, $registryMock],
             '',
             false,
@@ -311,7 +315,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         );
 
         $abstractModelMock->expects($this->once())->method('getData')->willReturn(['data' => 'value']);
-        $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\AdapterInterface::class);
+        $connectionMock = $this->getMock(AdapterInterface::class);
         $this->transactionManagerMock->expects($this->once())
             ->method('start')
             ->with($connectionInterfaceMock)
@@ -334,13 +338,13 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             $this->returnValue('tableName')
         );
         $mainTableReflection = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_mainTable'
         );
         $mainTableReflection->setAccessible(true);
         $mainTableReflection->setValue($this->_model, 'tableName');
         $idFieldNameReflection = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_idFieldName'
         );
         $idFieldNameReflection->setAccessible(true);
@@ -351,7 +355,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $abstractModelMock->expects($this->once())->method('afterDelete');
         $abstractModelMock->expects($this->once())->method('afterDeleteCommit');
         $this->assertInstanceOf(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             $this->_model->delete($abstractModelMock)
         );
     }
@@ -361,7 +365,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false);
         $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false);
         $abstractModelMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Model\AbstractModel::class,
+            AbstractModel::class,
             [$contextMock, $registryMock],
             '',
             false,
@@ -381,7 +385,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
     public function testGetDataChanged($getOriginData, $expected)
     {
         $connectionInterfaceMock = $this->getMock(
-            \Magento\Framework\DB\Adapter\AdapterInterface::class,
+            AdapterInterface::class,
             [],
             [],
             '',
@@ -393,7 +397,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $contextMock = $this->getMock(\Magento\Framework\Model\Context::class, [], [], '', false);
         $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false);
         $abstractModelMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Model\AbstractModel::class,
+            AbstractModel::class,
             [$contextMock, $registryMock],
             '',
             false,
@@ -402,7 +406,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             ['__wakeup', 'getOrigData', 'getData']
         );
         $mainTableProperty = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_mainTable'
         );
         $mainTableProperty->setAccessible(true);
@@ -431,13 +435,13 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
 
     public function testPrepareDataForUpdate()
     {
-        $connectionMock = $this->getMock(\Magento\Framework\DB\Adapter\AdapterInterface::class, [], [], '', false);
+        $connectionMock = $this->getMock(AdapterInterface::class, [], [], '', false);
         $context = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
             \Magento\Framework\Model\Context::class
         );
         $registryMock = $this->getMock(\Magento\Framework\Registry::class, [], [], '', false);
         $resourceMock = $this->getMock(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             [
                 '_construct',
                 'getConnection',
@@ -449,7 +453,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             false
         );
         $connectionInterfaceMock = $this->getMock(
-            \Magento\Framework\DB\Adapter\AdapterInterface::class,
+            AdapterInterface::class,
             [],
             [],
             '',
@@ -462,7 +466,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMockForAbstractClass();
         $abstractModelMock = $this->getMockForAbstractClass(
-            \Magento\Framework\Model\AbstractModel::class,
+            AbstractModel::class,
             [$context, $registryMock, $resourceMock, $resourceCollectionMock]
         );
         $data = 'tableName';
@@ -474,13 +478,13 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
             $this->returnValue('tableName')
         );
         $mainTableReflection = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_mainTable'
         );
         $mainTableReflection->setAccessible(true);
         $mainTableReflection->setValue($this->_model, 'tableName');
         $idFieldNameReflection = new \ReflectionProperty(
-            \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class,
+            AbstractDb::class,
             '_idFieldName'
         );
         $idFieldNameReflection->setAccessible(true);
@@ -540,7 +544,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         /**
          * Mock SUT so as not to test extraneous logic
          */
-        $model = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\Db\AbstractDb::class)
+        $model = $this->getMockBuilder(AbstractDb::class)
             ->disableOriginalConstructor()
             ->setMethods(['_prepareDataForSave', 'getIdFieldName', 'getConnection', 'getMainTable'])
             ->getMockForAbstractClass();
@@ -557,7 +561,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $reflectionProperty->setValue($model, $pkIncrement);
 
         // Mocked behavior
-        $connectionMock = $this->getMockBuilder(\Magento\Framework\DB\Adapter\AdapterInterface::class)
+        $connectionMock = $this->getMockBuilder(AdapterInterface::class)
             ->disableOriginalConstructor()
             ->setMethods(['lastInsertId'])
             ->getMockForAbstractClass();
@@ -579,7 +583,7 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
 
         //      Only set object id if not PK autoincrement
         $setIdInvokedCount = $pkIncrement ? 1 : 0;
-        $inputObject = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel::class)
+        $inputObject = $this->getMockBuilder(AbstractModel::class)
             ->disableOriginalConstructor()
             ->getMock();
         $inputObject->expects($this->exactly($setIdInvokedCount))->method('setId');
@@ -591,9 +595,37 @@ class AbstractDbTest extends \PHPUnit_Framework_TestCase
         $reflectionMethod->invokeArgs($model, [$inputObject]);
     }
 
+    /**
+     * @return array
+     */
     public function saveNewObjectDataProvider()
     {
         return [[true], [false]];
     }
 
+    /**
+     * @expectedException \Magento\Framework\Exception\AlreadyExistsException
+     */
+    public function testDuplicateExceptionProcessingOnSave()
+    {
+        $connection = $this->getMock(AdapterInterface::class);
+        $connection->expects($this->once())->method('rollback');
+
+        /** @var AbstractDb|\PHPUnit_Framework_MockObject_MockObject $model */
+        $model = $this->getMockBuilder(AbstractDb::class)
+            ->disableOriginalConstructor()
+            ->setMethods(['getConnection'])
+            ->getMockForAbstractClass();
+        $model->expects($this->any())->method('getConnection')->willReturn($connection);
+
+        /** @var AbstractModel|\PHPUnit_Framework_MockObject_MockObject $object */
+        $object = $this->getMockBuilder(AbstractModel::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $object->expects($this->once())->method('hasDataChanges')->willReturn(true);
+        $object->expects($this->once())->method('beforeSave')->willThrowException(new DuplicateException());
+        $object->expects($this->once())->method('setHasDataChanges')->with(true);
+
+        $model->save($object);
+    }
 }
diff --git a/setup/src/Magento/Setup/Model/PackagesData.php b/setup/src/Magento/Setup/Model/PackagesData.php
index 701393fd9666102147c5921ad1831a69488ba8fb..1fc8e9db2bf64f3874bbfa457eb3384d91425863 100644
--- a/setup/src/Magento/Setup/Model/PackagesData.php
+++ b/setup/src/Magento/Setup/Model/PackagesData.php
@@ -409,6 +409,7 @@ class PackagesData
                 return in_array(
                     $item['package_type'],
                     [
+                        \Magento\Setup\Model\Grid\TypeMapper::LANGUAGE_PACKAGE_TYPE,
                         \Magento\Setup\Model\Grid\TypeMapper::MODULE_PACKAGE_TYPE,
                         \Magento\Setup\Model\Grid\TypeMapper::EXTENSION_PACKAGE_TYPE,
                         \Magento\Setup\Model\Grid\TypeMapper::THEME_PACKAGE_TYPE,
@@ -491,26 +492,38 @@ class PackagesData
 
                 return array_keys($packageVersions);
             }
-        } else {
-            $versionsPattern = '/^versions\s*\:\s(.+)$/m';
-
-            $commandParams = [
-                self::PARAM_COMMAND => self::COMPOSER_SHOW,
-                self::PARAM_PACKAGE => $package,
-                self::PARAM_AVAILABLE => true
-            ];
-
-            $applicationFactory = $this->objectManagerProvider->get()
-                ->get(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class);
-            /** @var \Magento\Composer\MagentoComposerApplication $application */
-            $application = $applicationFactory->create();
-
-            $result = $application->runComposerCommand($commandParams);
-            $matches = [];
-            preg_match($versionsPattern, $result, $matches);
-            if (isset($matches[1])) {
-                return explode(', ', $matches[1]);
-            }
+        }
+
+        return $this->getAvailableVersionsFromAllRepositories($package);
+    }
+
+    /**
+     * Get available versions of package by "composer show" command
+     *
+     * @param string $package
+     * @return array
+     * @exception \RuntimeException
+     */
+    private function getAvailableVersionsFromAllRepositories($package)
+    {
+        $versionsPattern = '/^versions\s*\:\s(.+)$/m';
+
+        $commandParams = [
+            self::PARAM_COMMAND => self::COMPOSER_SHOW,
+            self::PARAM_PACKAGE => $package,
+            self::PARAM_AVAILABLE => true
+        ];
+
+        $applicationFactory = $this->objectManagerProvider->get()
+            ->get(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class);
+        /** @var \Magento\Composer\MagentoComposerApplication $application */
+        $application = $applicationFactory->create();
+
+        $result = $application->runComposerCommand($commandParams);
+        $matches = [];
+        preg_match($versionsPattern, $result, $matches);
+        if (isset($matches[1])) {
+            return explode(', ', $matches[1]);
         }
 
         throw new \RuntimeException(
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/PackagesDataTest.php b/setup/src/Magento/Setup/Test/Unit/Model/PackagesDataTest.php
index 5a7ead384665446fa7fb3dc82dff33d4b9777133..54a297e7dbe43909c0fe9e929aec95a0239beb73 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/PackagesDataTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/PackagesDataTest.php
@@ -22,38 +22,96 @@ class PackagesDataTest extends \PHPUnit_Framework_TestCase
      */
     private $packagesData;
 
+    /**
+     * @var ComposerInformation|MockObject
+     */
+    private $composerInformation;
+
+    /**
+     * @var \Magento\Setup\Model\DateTime\TimeZoneProvider|MockObject
+     */
+    private $timeZoneProvider;
+
+    /**
+     * @var \Magento\Setup\Model\PackagesAuth|MockObject
+     */
+    private $packagesAuth;
+
+    /**
+     * @var \Magento\Framework\Filesystem|MockObject
+     */
+    private $filesystem;
+
+    /**
+     * @var \Magento\Setup\Model\ObjectManagerProvider|MockObject
+     */
+    private $objectManagerProvider;
+
+    /**
+     * @var \Magento\Setup\Model\Grid\TypeMapper|MockObject
+     */
+    private $typeMapper;
+
     public function setUp()
     {
-        $composerInformation = $this->getComposerInformation();
-        $timeZoneProvider = $this->getMock(\Magento\Setup\Model\DateTime\TimeZoneProvider::class, [], [], '', false);
+        $this->composerInformation = $this->getComposerInformation();
+        $this->timeZoneProvider = $this->getMockBuilder(\Magento\Setup\Model\DateTime\TimeZoneProvider::class)
+            ->disableOriginalConstructor()
+            ->getMock();
         $timeZone = $this->getMock(\Magento\Framework\Stdlib\DateTime\Timezone::class, [], [], '', false);
-        $timeZoneProvider->expects($this->any())->method('get')->willReturn($timeZone);
-        $packagesAuth = $this->getMock(\Magento\Setup\Model\PackagesAuth::class, [], [], '', false);
-        $filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false);
-        $objectManagerProvider = $this->getMock(\Magento\Setup\Model\ObjectManagerProvider::class, [], [], '', false);
+        $this->timeZoneProvider->expects($this->any())->method('get')->willReturn($timeZone);
+        $this->packagesAuth = $this->getMock(\Magento\Setup\Model\PackagesAuth::class, [], [], '', false);
+        $this->filesystem = $this->getMock(\Magento\Framework\Filesystem::class, [], [], '', false);
+        $this->objectManagerProvider = $this->getMockBuilder(\Magento\Setup\Model\ObjectManagerProvider::class)
+            ->disableOriginalConstructor()
+            ->getMock();
         $objectManager = $this->getMockForAbstractClass(\Magento\Framework\ObjectManagerInterface::class);
-        $applicationFactory = $this->getMock(
-            \Magento\Framework\Composer\MagentoComposerApplicationFactory::class,
-            [],
-            [],
-            '',
-            false
-        );
+        $appFactory = $this->getMockBuilder(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class)
+            ->disableOriginalConstructor()
+            ->getMock();
         $application = $this->getMock(\Magento\Composer\MagentoComposerApplication::class, [], [], '', false);
         $application->expects($this->any())
             ->method('runComposerCommand')
-            ->willReturn('versions: 2.0.1');
-        $applicationFactory->expects($this->any())->method('create')->willReturn($application);
+            ->willReturnMap([
+                [
+                    [
+                        PackagesData::PARAM_COMMAND => PackagesData::COMPOSER_SHOW,
+                        PackagesData::PARAM_PACKAGE => 'magento/package-1',
+                        PackagesData::PARAM_AVAILABLE => true,
+                    ],
+                    null,
+                    'versions: 2.0.1'
+                ],
+                [
+                    [
+                        PackagesData::PARAM_COMMAND => PackagesData::COMPOSER_SHOW,
+                        PackagesData::PARAM_PACKAGE => 'magento/package-2',
+                        PackagesData::PARAM_AVAILABLE => true,
+                    ],
+                    null,
+                    'versions: 2.0.1'
+                ],
+                [
+                    [
+                        PackagesData::PARAM_COMMAND => PackagesData::COMPOSER_SHOW,
+                        PackagesData::PARAM_PACKAGE => 'partner/package-3',
+                        PackagesData::PARAM_AVAILABLE => true,
+                    ],
+                    null,
+                    'versions: 3.0.1'
+                ],
+            ]);
+        $appFactory->expects($this->any())->method('create')->willReturn($application);
         $objectManager->expects($this->any())
             ->method('get')
             ->with(\Magento\Framework\Composer\MagentoComposerApplicationFactory::class)
-            ->willReturn($applicationFactory);
-        $objectManagerProvider->expects($this->any())->method('get')->willReturn($objectManager);
+            ->willReturn($appFactory);
+        $this->objectManagerProvider->expects($this->any())->method('get')->willReturn($objectManager);
 
         $directoryWrite = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\WriteInterface::class);
         $directoryRead = $this->getMockForAbstractClass(\Magento\Framework\Filesystem\Directory\ReadInterface::class);
-        $filesystem->expects($this->any())->method('getDirectoryRead')->will($this->returnValue($directoryRead));
-        $filesystem->expects($this->any())
+        $this->filesystem->expects($this->any())->method('getDirectoryRead')->will($this->returnValue($directoryRead));
+        $this->filesystem->expects($this->any())
             ->method('getDirectoryWrite')
             ->will($this->returnValue($directoryWrite));
         $directoryWrite->expects($this->any())->method('isExist')->willReturn(true);
@@ -81,32 +139,41 @@ class PackagesDataTest extends \PHPUnit_Framework_TestCase
                 . '}}}'
             );
 
-        $typeMapper = $this->getMockBuilder(\Magento\Setup\Model\Grid\TypeMapper::class)
+        $this->typeMapper = $this->getMockBuilder(\Magento\Setup\Model\Grid\TypeMapper::class)
             ->disableOriginalConstructor()
             ->getMock();
-        $typeMapper->expects(static::any())
+        $this->typeMapper->expects(static::any())
             ->method('map')
             ->willReturnMap([
                 [ComposerInformation::MODULE_PACKAGE_TYPE, \Magento\Setup\Model\Grid\TypeMapper::MODULE_PACKAGE_TYPE],
             ]);
 
+        $this->createPackagesData();
+    }
+
+    private function createPackagesData()
+    {
         $this->packagesData = new PackagesData(
-            $composerInformation,
-            $timeZoneProvider,
-            $packagesAuth,
-            $filesystem,
-            $objectManagerProvider,
-            $typeMapper
+            $this->composerInformation,
+            $this->timeZoneProvider,
+            $this->packagesAuth,
+            $this->filesystem,
+            $this->objectManagerProvider,
+            $this->typeMapper
         );
     }
 
     /**
+     * @param array $requiredPackages
+     * @param array $installedPackages
+     * @param array $repo
      * @return ComposerInformation|MockObject
      */
-    private function getComposerInformation()
+    private function getComposerInformation($requiredPackages = [], $installedPackages = [], $repo = [])
     {
         $composerInformation = $this->getMock(ComposerInformation::class, [], [], '', false);
         $composerInformation->expects($this->any())->method('getInstalledMagentoPackages')->willReturn(
+            $installedPackages ?:
             [
                 'magento/package-1' => [
                     'name' => 'magento/package-1',
@@ -117,21 +184,30 @@ class PackagesDataTest extends \PHPUnit_Framework_TestCase
                     'name' => 'magento/package-2',
                     'type' => 'magento2-module',
                     'version'=> '1.0.1'
-                ]
+                ],
+                'partner/package-3' => [
+                    'name' => 'partner/package-3',
+                    'type' => 'magento2-module',
+                    'version'=> '3.0.0'
+                ],
             ]
         );
 
         $composerInformation->expects($this->any())->method('getRootRepositories')
-            ->willReturn(['repo1', 'repo2']);
+            ->willReturn($repo ?: ['repo1', 'repo2']);
         $composerInformation->expects($this->any())->method('getPackagesTypes')
             ->willReturn(['magento2-module']);
         $rootPackage = $this->getMock(RootPackage::class, [], ['magento/project', '2.1.0', '2']);
         $rootPackage->expects($this->any())
             ->method('getRequires')
-            ->willReturn([
-                'magento/package-1' => '1.0.0',
-                'magento/package-2' => '1.0.1'
-            ]);
+            ->willReturn(
+                $requiredPackages ?:
+                [
+                    'magento/package-1' => '1.0.0',
+                    'magento/package-2' => '1.0.1',
+                    'partner/package-3' => '3.0.0',
+                ]
+            );
         $composerInformation->expects($this->any())
             ->method('getRootPackage')
             ->willReturn($rootPackage);
@@ -146,19 +222,57 @@ class PackagesDataTest extends \PHPUnit_Framework_TestCase
         $this->assertArrayHasKey('date', $latestData['lastSyncDate']);
         $this->assertArrayHasKey('time', $latestData['lastSyncDate']);
         $this->assertArrayHasKey('packages', $latestData);
-        $this->assertSame(2, count($latestData['packages']));
-        $this->assertSame(2, $latestData['countOfUpdate']);
+        $this->assertSame(3, count($latestData['packages']));
+        $this->assertSame(3, $latestData['countOfUpdate']);
         $this->assertArrayHasKey('installPackages', $latestData);
         $this->assertSame(1, count($latestData['installPackages']));
         $this->assertSame(1, $latestData['countOfInstall']);
     }
 
-    public function testGetPackagesForUpdate()
+    /**
+     * @expectedException \RuntimeException
+     * @expectedExceptionMessage Couldn't get available versions for package partner/package-4
+     */
+    public function testGetPackagesForUpdateWithException()
     {
+        $requiredPackages = [
+            'partner/package-4' => '4.0.4',
+        ];
+        $installedPackages = [
+            'partner/package-4' => [
+                'name' => 'partner/package-4',
+                'type' => 'magento2-module',
+                'version'=> '4.0.4'
+            ],
+        ];
+        $this->composerInformation = $this->getComposerInformation($requiredPackages, $installedPackages);
+        $this->createPackagesData();
+        $this->packagesData->getPackagesForUpdate();
+    }
+
+    public function testPackagesForUpdateFromJson()
+    {
+        $this->composerInformation = $this->getComposerInformation([], [], ['https://repo1']);
+        $this->packagesAuth->expects($this->atLeastOnce())
+            ->method('getCredentialBaseUrl')
+            ->willReturn('repo1');
+        $this->createPackagesData();
         $packages = $this->packagesData->getPackagesForUpdate();
         $this->assertEquals(2, count($packages));
         $this->assertArrayHasKey('magento/package-1', $packages);
+        $this->assertArrayHasKey('partner/package-3', $packages);
+        $firstPackage = array_values($packages)[0];
+        $this->assertArrayHasKey('latestVersion', $firstPackage);
+        $this->assertArrayHasKey('versions', $firstPackage);
+    }
+
+    public function testGetPackagesForUpdate()
+    {
+        $packages = $this->packagesData->getPackagesForUpdate();
+        $this->assertEquals(3, count($packages));
+        $this->assertArrayHasKey('magento/package-1', $packages);
         $this->assertArrayHasKey('magento/package-2', $packages);
+        $this->assertArrayHasKey('partner/package-3', $packages);
         $firstPackage = array_values($packages)[0];
         $this->assertArrayHasKey('latestVersion', $firstPackage);
         $this->assertArrayHasKey('versions', $firstPackage);
@@ -167,9 +281,10 @@ class PackagesDataTest extends \PHPUnit_Framework_TestCase
     public function testGetInstalledPackages()
     {
         $installedPackages = $this->packagesData->getInstalledPackages();
-        $this->assertEquals(2, count($installedPackages));
+        $this->assertEquals(3, count($installedPackages));
         $this->assertArrayHasKey('magento/package-1', $installedPackages);
         $this->assertArrayHasKey('magento/package-2', $installedPackages);
+        $this->assertArrayHasKey('partner/package-3', $installedPackages);
     }
 
     public function testGetMetaPackagesMap()