Sunday, February 25, 2018

Eloquent taking too long with remote MySQL DB

Leave a Comment

I'm developing a web (Laravel) and iOS app that both consumes from a REST API that I'm also developing with Laravel. The API queries from a MySQL Database hosted at an AWS RDS instance.

When I set up both, the API and the apps on localhost and the API pointing the my local Database (same machine as the apps and API) it works ok as expected, but the problem is when I setup the API to query from the AWS RDS Database instead of locally. Simple queries made with eloquent, like

Product::where('brand_id', '=', $id)                ->get() 

takes like 10 - 15 mins to complete, but if I use the query builder, for expample;

DB::select('select * from products where brand_id = ?', [$id]); 

works fine.

Executing the query directly on MySQL or running Product::all() at the Laravel Tinker works as intended with local API / Remote DB setup.

EDIT:

Here is what I get when I execute SHOW CREATE TABLE products

CREATE TABLE `products` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `SKU` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `price` double(8,2) NOT NULL,   `description` text COLLATE utf8mb4_unicode_ci NOT NULL,   `product_details` text COLLATE utf8mb4_unicode_ci NOT NULL,   `material_and_care` text COLLATE utf8mb4_unicode_ci NOT NULL,   `material` text COLLATE utf8mb4_unicode_ci NOT NULL,   `care` text COLLATE utf8mb4_unicode_ci NOT NULL,   `colors` text COLLATE utf8mb4_unicode_ci NOT NULL,   `made_in` text COLLATE utf8mb4_unicode_ci NOT NULL,   `size_chart` text COLLATE utf8mb4_unicode_ci,   `size_chart_url` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,   `brand_id` int(10) unsigned NOT NULL,   `category_id` int(10) unsigned NOT NULL,   `published` tinyint(1) NOT NULL DEFAULT '0',   `featured` tinyint(1) NOT NULL DEFAULT '0',   `promo_title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `promo_caption` text COLLATE utf8mb4_unicode_ci NOT NULL,   `shipping_height` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `shipping_width` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `shipping_length` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `shipping_weight` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,   `is_vertical` tinyint(1) NOT NULL,   `created_at` timestamp NULL DEFAULT NULL,   `updated_at` timestamp NULL DEFAULT NULL,   `deleted_at` timestamp NULL DEFAULT NULL,   PRIMARY KEY (`id`),   KEY `products_category_id_foreign` (`category_id`),   KEY `products_brand_id_foreign` (`brand_id`),   CONSTRAINT `products_brand_id_foreign` FOREIGN KEY (`brand_id`) REFERENCES `brands` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,   CONSTRAINT `products_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `subcategories` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=501 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci 

My Product Model:

class Product extends Model {     use SoftDeletes, CascadeSoftDeletes;      protected $cascadeDeletes = [         'items',         'options',         'images',         'sizes'     ];      protected $fillable = [         'sku',         'name',         'price',         'description',         'product_details',         'material_and_care',         'material',         'color_1',         'color_2',         'color_3',         'care',         'made_in',         'size_chart',         'size_chart_url',         'brand_id',         'category_id',         'published',         'featured',         'promo_title',         'promo_caption',         'shipping_weight',         'shipping_height',         'shipping_width',         'shipping_length',         'is_vertical'     ];      protected $appends = [         'interests',         'brand',         'options',         'sizes',         'items',         'images',         'comment_count',         'discount',         'suits_me_count'     ];      protected $hidden = [         'created_at',         'deleted_at',         'updated_at',         'subcategory'     ];      protected static function boot()     {         parent::boot();         static::addGlobalScope(new PublishedProductScope);     }      public function getDiscountAttribute() {         return $this->discount()->first();     }      public function getSuitsMeCountAttribute() {         return $this->suitsmes()->count();     }      public function getCommentCountAttribute() {         return $this->comments()->count();     }       public function getImagesAttribute(){         return $this->images()->pluck("url");     }      public function getInterestsAttribute() {         return $this->interests()->get();     }      public function getBrandAttribute(){         return $this->brand()->first();     }      public function getOptionsAttribute(){         return $this->options()->get();     }      public function getSizesAttribute(){         return $this->sizes()->get();     }      public function getItemsAttribute(){         return $this->items()->get();     }      public function interests() {         return $this->belongsToMany('App\Interest', 'product_interest');     }      public function brand() {         return $this->belongsTo('App\Brand');     }      public function options() {         return $this->hasMany('App\ProductOption');     }      public function sizes() {         return $this->hasMany('App\ProductSize');     }      public function items() {         return $this->hasMany('App\ProductItem');     }      public function images() {         return $this->hasMany('App\ProductImage');     }      public function comments() {         return $this->hasMany('App\ProductComment');     }      public function suitsmes() {         return $this->belongsToMany('App\User', 'wishlist', 'product_id', 'user_id');     }      public function discount(){         return $this->hasOne('App\Discount');     }      public function category() {         return $this->belongsTo('App\Subcategory', 'category_id');     }  } 

And my Brand Model:

class Brand extends Model {    protected $fillable = [         'first_name',         'last_name',         'email',         'phone_number',         'birth_date',         'ssn',         'street_address',         'street_address_2',         'city_address',         'state_address',         'postal_code_address',         'legal_name',         'dba',         'tax_id',         'street_business_address',         'street_business_address_2',         'city_business_address',         'state_business_address',         'postal_code_business_address',         'destination_fund',         'email_business',         'phone_number_business',         'account_number_fund',         'routing_number_fund'     ];      protected $hidden = [         'created_at',         'deleted_at',         'updated_at'     ];      protected $appends = [         'images'     ];      public function getImagesAttribute()     {         return $this->images()->get();     }      public function getBillboardPicturesAttribute() {         $pictures = [$this->billboard, $this->billboard2, $this->billboard3, $this->billboard4];         return $pictures;     }          public function users()     {         return $this->belongsToMany('App\User', 'user_brand_role', 'brand_id', 'user_id');     }      public function getInterestsAttribute() {         return $this->interests()->pluck('interest_id');     }      public function interests() {         return $this->belongsToMany('App\Interest', 'brand_interest', 'brand_id', 'interest_id');     }      public function products() {         return $this->hasMany('App\Product');     }      public function images() {         return $this->hasMany('App\BrandImage');     }      public function categories() {         return $this->hasMany('App\Category');     }      public function getCatalogAttribute() {         return $this->categories()->orderBy('name', 'asc')->get();     }   } 

1 Answers

Answers 1

The issue is this piece of code in your Product model:

protected $appends = [     'interests',     'brand',     'options',     'sizes',     'items',     'images',     'comment_count',     'discount',     'suits_me_count' ]; 

These properties are relationships, and so they result in multiple joins every time Eloquent reads/returns a row.

This query: DB::select('select * from products where brand_id = ?', [$id]); doesn't result in any joins and is a select query from a single table. That's why when you compare results, you find a dramatic difference in query time.

There are 2 possible solutions:

1. Remove the appends

Remove the appends and whenever you need a relationship use the with function

2. Make the append properties hidden

You could add this code to your Product Model:

protected $hidden = [     'created_at',     'deleted_at',     'updated_at',     'interests',     'brand',     'options',     'sizes',     'items',     'images',     'comment_count',     'discount',     'suits_me_count' ]; 

When you need a property just use $product->makeVisible('property_name'). Otherwise, by default, these properties won't be loaded or queried

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment