Saturday, August 24, 2013

Load Balancing Issue with Symfony2 and Session

I am going to explain the problem with symfony2 and a load balancing scenario.

If  you are using Round Robin load balancing technique for symfony2 application, your session storage must be database driven.

Below is the configuration for database driven session storage in symfony2 application:

# app/config/config.yml
framework:
    session:
        storage_id: session.storage.pdo

parameters:
    pdo.db_options:
        db_table: session
        db_id_col: session_id
        db_data_col: session_value
        db_time_col: session_time

services:
    pdo:
        class: PDO
        arguments:
            dsn:      "mysql:dbname=mydatabase"
            user:     myuser
            password: mypassword

    session.storage.pdo:
        class: Symfony\Component\HttpFoundation\SessionStorage\PdoSessionStorage
        arguments: ["@pdo", "%session.storage.options%", "%pdo.db_options%"]


Here is the reference article about this: How to use PdoSessionStorage to store Sessions in the Database

Sunday, June 9, 2013

SwiftMailer/Symfony2: Expected response code 250 but got code "421", with message "421 Timeout waiting for data from client.

I assume you are using Symfony2 and SwiftMailer to send emails via crontab and symfony2 command. I was using For-Loop in Symfony2 command line to send over hundreds of emails with AWS SES, but the error below was popping up randomly:

Expected response code 250 but got code "421", with message "421 Timeout waiting for data from client

After some search and knocking my head against the wall, I found the answer:

$this->getContainer()->get('mailer')->getTransport()->start();
$this->getContainer()->get('mailer')->send($message);
$this->getContainer()->get('mailer')->getTransport()->stop();

Yes, that's it. You need to start and stop the connection each time. I know it sucks, but this fixed my random error.

My entire command class would like something like below:

class TestEmailCommand extends ContainerAwareCommand
{

    protected function configure()
    {
        $this
                ->setName('test:email')
                ->setDescription('Test Email System')
        ;
    }

    protected function execute(InputInterface $input , OutputInterface $output)
    {

        $em = $this->getContainer()->get("doctrine")->getManager();

        $users = $em->getRepository('UserBundle:User')->findAllforEmails();

        foreach($users as $user)
        {

            $message = \Swift_Message::newInstance()
                    ->setSubject('Dummy Subject')
                    ->setFrom('dummy@example.com')
                    ->setTo($user->getEmail())
                    ->setBody('Dummy Body of the Email' , 'text/html');

            try
            {

                $this->getContainer()->get('mailer')->getTransport()->start();
                $this->getContainer()->get('mailer')->send($message);
                $this->getContainer()->get('mailer')->getTransport()->stop();
            }
            catch(\Exception $exc)
            {
                $output->writeln($exc->getTraceAsString());
            }
        }
    }
}
Promote Your Blog

Sunday, January 27, 2013

Installing Gearman on Amazon's EC2 Linux AMI


Steps below show how to install Gearman on Amazon Linux AMI. I assume you are logged in as root or you may want to use sudo. 

1. cd /var/tmp;
2. wget https://launchpad.net/gearmand/1.2/1.1.4/+download/gearmand-1.1.4.tar.gz
3. yum install libevent-devel gcc-c++ boost-devel libuuid-devel memcached-devel gperf
4. tar xvzf gearmand-1.1.4.tar.gz
5. cd gearmand-1.1.4
6. ./configure --prefix=/usr
7. sudo make && sudo make install
8. adduser gearmand
9. /usr/sbin/gearmand -u gearmand
10.  pecl channel-update pecl.php.net
11.  pecl install channel://pecl.php.net/gearman-1.1.1
12.  php --ini
13.  echo "extension=gearman.so" >> /etc/php.ini
14.  /etc/rc.d/init.d/httpd restart


Some useful commands:

php --info | grep gear

You may find different versions here and replace it with the wget link above.

Thanks to: http://mysqldba.blogspot.com/2011/05/installing-gearmand-on-amazons-ec2.html

344964_Post to 40+ Social Networks

Monday, August 13, 2012

How to Install PHP APC (Alternative PHP Cache) on Linux

Steps below show how to install PHP: APC on Linux :

  1. sudo yum install php-pear
  2. sudo yum install php-devel
  3. sudo yum install httpd-devel
  4. sudo pecl install apc
  5. Add extension=apc.so to end of php.ini
  6. sudo /etc/init.d/httpd restart

To check if APC is installed, just run command below:

php -r 'phpinfo();' | grep apc

Ref: http://2bits.com/articles/installing-php-apc-gnulinux-centos-5.html

Wednesday, July 4, 2012

Symfony2 Many-To-Many Relation with extra fields Form Handling

This is going to be a solution for Many-To-Many relation that needs to be Many-To-One / One-To-Many instead, since some extra fields need to be saved in the intermediate table. I assume you have basic knowledge of Symfony2, Doctrine. 

For this example I have three tables Product, Order, ProductOrderOrder has many Products, Product has many Orders and ProductOrder table will save the association in an intermediate table.



The source code will create a form like below which shows new Order with list of existing/created Products and user can choose product(s) and create a new Order. In controller class I have just added the create and update action. I hope this helps. 

The point here is when you submit a form, setProduct set all selected products and when you are in EDIT page the getProduct return all the associated products. In updateAction  remove the previous associated products and add new ones. I could not find a way not to remove and add again.  

GitHub: https://github.com/pmoubed/symfony2_tutorial



Promote Your Blog Entity\Order.php

<?php

namespace PMI\TestBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use PMI\TestBundle\Entity\ProductOrder;


/**
 * @ORM\Entity
 * @ORM\Table(name="order_")
 */
class Order
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @var integer $id
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length="255", name="first_name")
     * @Assert\NotBlank()
     * @var string $name
     * 
     */
    protected $name;

    /**
     * @ORM\OneToMany(targetEntity="ProductOrder", mappedBy="order", cascade={"all"})
     * */
    protected $po;

    protected $products;

    public function __construct()
    {
        $this->po = new ArrayCollection();
        $this->products = new ArrayCollection();
    }

    // Getters and Setters

    public function __toString()
    {
        return $this->name;
    }

    // Important 
    public function getProduct()
    {
        $products = new ArrayCollection();
        
        foreach($this->po as $p)
        {
            $products[] = $p->getProduct();
        }

        return $products;
    }
    // Important
    public function setProduct($products)
    {
        foreach($products as $p)
        {
            $po = new ProductOrder();

            $po->setOrder($this);
            $po->setProduct($p);

            $this->addPo($po);
        }

    }

    public function getOrder()
    {
        return $this;
    }

    public function addPo($ProductOrder)
    {
        $this->po[] = $ProductOrder;
    }
    
    public function removePo($ProductOrder)
    {
        return $this->po->removeElement($ProductOrder);
    }


}

Entity\Product.php

<?php

namespace PMI\TestBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;

use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     * 
     * @var integer $id
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length="255")
     * @var string $firstName
     * 
     */
    protected $name;

  
    /**
     * @ORM\OneToMany(targetEntity="ProductOrder" , mappedBy="product" , cascade={"all"})
     * */
    protected $po;
    

    public function __construct()
    {

    }
    
    // Getters and Setters 
          
    public function __toString()
    {
        return $this->name;
    }



}

Entity\ProductOrder.php

<?php

namespace PMI\TestBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use PMI\LayerBundle\Entity\Composite;


/**
 * @ORM\Entity
 * @ORM\Table(name="p_o")
 * @ORM\HasLifecycleCallbacks()
 */
class ProductOrder
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     *
     * @var integer $id
     */
    protected $id;


    /**
     * @ORM\ManyToOne(targetEntity="Product", inversedBy="po")
     * @ORM\JoinColumn(name="p_id", referencedColumnName="id")
     * */
    protected $product;

    /**
     * @ORM\ManyToOne(targetEntity="Order", inversedBy="po")
     * @ORM\JoinColumn(name="o_id", referencedColumnName="id")
     * */
    protected $order;


    // Getter, Setters, _Construct, __toString 


}

Form\OrderType.php

<?php

namespace PMI\TestBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;


class OrderType extends AbstractType
{

    public function buildForm(FormBuilder $builder , array $options)
    {

        $builder
                ->add('name')
                ->add('Product' , 'entity' , array(
                      'class'    => 'PMITestBundle:Product' ,
                      'property' => 'name' ,
                      'expanded' => true ,
                      'multiple' => true , ))
        ;
    }

    public function getName()
    {
        return 'pmi_testbundle_ordertype';
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' => 'PMI\TestBundle\Entity\Order' ,
            'em'         => '' ,
        );
    }


}


Controller\OrderController.php

<?php

namespace PMI\TestBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use PMI\TestBundle\Entity\Order;
use PMI\TestBundle\Form\OrderType;
use PMI\TestBundle\Entity\ProductOrder;
use PMI\TestBundle\Form\ProductOrderType;


/**
 * Order controller.
 *
 * @Route("/order")
 */
class OrderController extends Controller
{


    /**
     * Displays a form to create a new Order entity.
     *
     * @Route("/new", name="test_order_new")
     * @Template()
     */
    public function newAction()
    {
        $entity = new Order();
        $form   = $this->createForm(new OrderType() , $entity);

        return array(
            'entity' => $entity ,
            'form'   => $form->createView()
        );
    }

    /**
     * Creates a new Order entity.
     *
     * @Route("/create", name="test_order_create")
     * @Method("post")
     * @Template("PMITestBundle:Order:new.html.twig")
     */
    public function createAction()
    {
        $order   = new Order();
        $request = $this->getRequest();
        $form    = $this->createForm(new OrderType() , $order);
        $form->bindRequest($request);

        $em = $this->getDoctrine()->getEntityManager();

        if($form->isValid())
        {

            $em->persist($order);
            $em->flush();

return $this->redirect($this->generateUrl('test_order_show' , array( 'id' => $order->getId() )));
        }

        return array(
            'entity' => $order ,
            'form'   => $form->createView()
        );
    }

    /**
     * Displays a form to edit an existing Order entity.
     *
     * @Route("/{id}/edit", name="test_order_edit")
     * @Template()
     */
    public function editAction($id)
    {
        $em = $this->getDoctrine()->getEntityManager();

        $entity = $em->getRepository('PMITestBundle:Order')->find($id);

        if(!$entity)
        {
            throw $this->createNotFoundException('Unable to find Order entity.');
        }


        $editForm = $this->createForm(new OrderType() , $entity , array( 'em' => $em ));

        $deleteForm = $this->createDeleteForm($id);

        return array(
            'entity'      => $entity ,
            'edit_form'   => $editForm->createView() ,
            'delete_form' => $deleteForm->createView() ,
        );
    }

    /**
     * Edits an existing Order entity.
     *
     * @Route("/{id}/update", name="test_order_update")
     * @Method("post")
     * @Template("PMITestBundle:Order:edit.html.twig")
     */
    public function updateAction($id)
    {
        $em = $this->getDoctrine()->getEntityManager();

        /* @var $entity Order */
        $entity = $em->getRepository('PMITestBundle:Order')->find($id);

        if(!$entity)
        {
            throw $this->createNotFoundException('Unable to find Order entity.');
        }

        $editForm   = $this->createForm(new OrderType() , $entity);
        $deleteForm = $this->createDeleteForm($id);

        $previousCollections = $entity->getPo();
        $previousCollections = $previousCollections->toArray();

        $request = $this->getRequest();

        $editForm->bindRequest($request);

        foreach($previousCollections as $po)
        {
            $entity->removePo($po);
        }

        if($editForm->isValid())
        {
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('test_order_edit' , array( 'id' => $id )));
        }

        return array(
            'entity'      => $entity ,
            'edit_form'   => $editForm->createView() ,
            'delete_form' => $deleteForm->createView() ,
        );
    }


}



Friday, June 15, 2012

Save MySQL query results from console

I assume you have logged into your MySQL console. You can save your query result into a file like below:

Text File 

SELECT * FROM your_table .
INTO OUTFILE '/tmp/file.txt'


CSV File

SELECT * FROM your_table 
       INTO OUTFILE '/tmp/file.csv'
       FIELDS TERMINATED BY ','
       ENCLOSED BY '"'
       LINES TERMINATED BY '\n'




Reference : http://www.tech-recipes.com/rx/1475/save-mysql-query-results-into-a-text-or-csv-file/

Monday, May 21, 2012

Amazon EC2 Micro Instance Swap Space - Linux

I have a Amazon EC2 Linux Micro instance. Since Micro instances have only 613MB of memory, MySQL crashed every now and then. After a long search about MySQL, Micro Instance and Memory Managment I found out there is no default SWAP space for Micro instance. So if you want to avoid the crash you may need to setup a swap space for your micro instance. Actually performance wise is better to enable swap.

Steps below show how to make a swap space for your Micro instance. I assume you have AWS Account with a Micro instance running.

  1. Run dd if=/dev/zero of=/swapfile bs=1M count=1024
  2. Run mkswap /swapfile
  3. Run swapon /swapfile
  4. Add this line /swapfile swap swap defaults 0 0 to /etc/fstab  
Step 4 is needed if you would like to automatically enable swap file after each reboot. 

Some useful command related to SWAP space: