Symfony 2 AppCache behaving differently when under heavy load - http

I am experiencing a strange problem with symfony 2. I have an app which has a count down timer on it (counting down hours minutes and days).
This minute by minute timer works perfectly 90% of the time, but when under heavy load, the timer got stuck and wouldn't change (presumably because it was serving a cached version):
We have been unable to replicate the error using a load testing script, it seems to only happen when there are lots of actual users on the site.
Our cache options are setup as follows:
class AppCache extends HttpCache
{
protected function getOptions()
{
return array(
'debug' => true,
'default_ttl' => 1 * 60 * 60,
'private_headers' => array('Authorization', 'Cookie'),
'allow_reload' => false,
'allow_revalidate' => false,
'stale_while_revalidate' => 2,
'stale_if_error' => 60,
);
}
}
The response that is being sporadically cached has the following header:
HTTP/1.1 200 OK
Date: Fri, 17 May 2013 09:36:55 GMT
Server: Apache/2.2.16 (Debian)
X-Powered-By: PHP/5.3.3-7+squeeze15
P3P: CP="CURa CUR NID ADM ADMa DEV DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"
Cache-Control: private
X-Symfony-Cache: GET /my-app/show: miss
Vary: Accept-Encoding
Content-Encoding: gzip
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8
Our Front Controller script file is as follows:
<?php header('P3P: CP="CURa CUR NID ADM ADMa DEV DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\ClassLoader\ApcClassLoader;
$loader = require_once __DIR__.'/app/bootstrap.php.cache';
require_once __DIR__.'/app/AppKernel.php';
require_once __DIR__.'/app/AppCache.php';
$loader = new ApcClassLoader('sf2', $loader);
$loader->register(true);
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel = new AppCache($kernel);
Request::enableHttpMethodParameterOverride();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
We haven't explicitly set any http caching on this action, or set anything to be cached apart from the fact that we are using the AppCache to wrap our kernel. Doing this definitely makes a massive difference to the performance of our site, as when we take it off, the load goes up very quickly during load testing.

Related

Some authenticated users can't access secure content or use the restricted API

I'm in desperate need for help on this.
I'm the developer of a site (rockalingua.com) that offers subscriptions to a Learning Management system. We have developed the LMS creating several drupal modules.
It's working quite well so far but we are getting reports from some schools that some students (authenticated users) can't access some content (They get messages stating that they don't have permission to see that content)
This seems to be random, but when this happens the site acts as if they where not logged in. If the log-in again they can access the content usually (they report they sometimes have to restart the browser for that to happen)
The pages that have this problem use a REST API created by us for tracking the students progress.
Searching the logs I realized that some time after a class starts using the system a lot of warning and notice messages are dumped to the messages log reporting unauthenticated users trying to use the API from the same IPs and urls seconds or minutes before, authenticated users where accessing it without any problem.
I've examined all the code involved several times and can't find anything wrong with it, specially when it seems to happen randomly.
I know most schools have a proxy that use a cache. But don't know much about proxies or whether they could be affecting this issue.
If so, what could be do to avoid school proxies caching dynamic pages?
UPDATE:
I include the typical response headers we are sending on the typical affected requests, and some relevant code:
Accept-Ranges: bytes
Age: 0
Cache-Control: no-cache, must-revalidate
Content-Encoding: gzip
Content-Language: en
Content-Length: 4590
Content-Type: text/html; charset=utf-8
Date: Fri, 26 Jan 2018 08:53:52 GMT
Expires: Sun, 19 Nov 1978 05:00:00 GMT
Server: Apache
Vary: Accept-Encoding
Via: 1.1 varnish
X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Powered-By: PHP/5.4.45
X-UA-Compatible: IE=Edge,chrome=1
X-Varnish: 1592956310
And here you have the code for a typical AJAX request that I have seem to behave as described sometimes:
// ...
if($form_state['data']['is_student']) {
$form['submit'] = [
'#type' => 'submit',
'#value' => t('Go back'),
'#attributes' => [
'class' => [$form_state['data']['activity_type']],
],
'#ajax' => [
'callback' => 'activity_modal_form_submit_callback'
],
];
} else {
}
return $form;
}
/* Ajax callback to update the activity item after closing its modal */
function activity_modal_form_submit_callback($form, &$form_state) {
ctools_include('modal');
ctools_include('ajax');
global $user;
$activities_retriever = new student_task_activities($user->uid, $form_state['data']['task_id']);
$activity = $activities_retriever->lms_student_task_activity($form_state['data']['activity_id']);
$activity_data = array(
//'activity_name' => $activity['title'],
'times' => $activities_retriever->lms_student_task_activity_repetitions_assigned($activity['activity_id']),
'task_activity_entity_id' => $activity['activity_entity'],
'type' => $activity['activity_type'],
'title' => $activity['activity_title'],
'status' => $activity['status'],
'progress' => $activity['progress'],
'is_correctness_supported' => $activity['is_game_supported'],
'percent_right' => $activity['percent_right'],
'is_student' => $form_state['data']['is_student'],
);
$item_updated = render_activity_item(
$form_state['data']['activity_id'],
$activity_data,
$form_state['data']['task_id'],
$form_state['data']['is_student'],
$form_state['data']['due_date'], false);
$commands[] = ctools_modal_command_dismiss();
if($activity_data['type'] == 'video' || $activity['activity_type'] == 'song') {
$commands[] = ajax_command_replace('#activity_' . $form_state['data']['activity_id'], $item_updated);
}
print ajax_render($commands);
drupal_exit();
}
As it calls student_task_activities needs the uid on the constructor to retrieve the activity data and in the described situations it receives a 0, the activity array is empty and when trying to render the activity item a notice is dumped to the log warning about the missing information. There is where it says the uid is 0 and that's how I realized the user id was lost somewhere in the process.

Response returned only after kernel.terminate event

My understanding of kernel.terminate is that it triggers after the response has been returned to the client.
In my testing tough, this does not appear to be the case. If I put a sleep(10) in the function that's called on kernel.terminate. the browser also waits for 10 seconds. The processing seems to be happening before the response is sent.
I have the following in config:
calendar:
class: Acme\CalendarBundle\Service\CalendarService
arguments: [ #odm.document_manager, #logger, #security.context, #event_dispatcher ]
tags:
- { name: kernel.event_subscriber }
My subscriber class:
class CalendarService implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
'kernel.terminate' => 'onKernelTerminate'
);
}
public function onKernelTerminate()
{
sleep(10);
echo "hello";
}
}
UPDATE
This appears to be related to Symfony not sending a Content-Length header. If I generate that, the response return properly.
// app_dev.php
...
$kernel = new AppKernel('dev', true);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
// --- START EDITS ---
$size = strlen($response->getContent());
$response->headers->set('Content-Length', $size);
$response->headers->set('Connection', 'close');
// ---- END EDITS ----
$response->send();
$kernel->terminate($request, $response);
This issue turned out to be very specific to my setup (Nginx, PHP-FCGI, Symfony).
There were a handful of issues in play that caused the issue:
Symfony does not include a Content-Length nor Connection: close header
PHP-FCGI does not support the fastcgi_finish_request function
Nginx buffers the response from PHP-FCGI because Gzip is on
The solution was to switch from PHP-FCGI to PHP-FPM in order to get support for fastcgi_finish_request. Symfony internally calls this before executing the kernel terminate logic thereby definitively closing the connection.
Another way to solve this would be to turn off Gzip on Nginx, but this wasn't really an option for me.

using swift mailer of symfony 2, got an email but getting unnecessary response in email

getting this response by email "HTTP/1.0 200 OK Cache-Control: no-cache Content-Type: text/html; charset=UTF-8 Date: Tue, 13 Nov 2012 04:56:14 GMT".
here is my code:
public function sendEmail($subject, $template, $templateParams)
{
$userEmail = $this->session->get('email');
$name = $this->session->get('name');
$adminEmail = $this->container;
$templateParams['username'] = $name;
$message = \Swift_Message::newInstance()
->setSubject($subject)
->setFrom($adminEmail)
->setTo($userEmail)
->setBody($this->templating->render('SocialDonSocialBundle:Email:'.$template,$templateParams), 'text/html');
$this->mailer->send($message);
Also note that this method is belongs to a service namely "Email". I have created a service "Email " which responsible to send emails. Does anybody know what might be the issue??
You need to use renderView() instead of render(), because render() always display the header.
In newer versions of Symfony2 like version 2.5.*
The solution is to use renderResponse and do a getContent() on it :
$content = $this->container->get('templating')->renderResponse(
'YOUR_TEMPLATE.twig',
templateParams)
)->getContent();
or with the same values like in the question :
$this->templating->renderResponse('SocialDonSocialBundle:Email:'.$template,$templateParams)->getContent();

Additional mailer service to use the spool and send instant emails in Symfony2 - strange headers

by default I use spool mailing solution for sending newsletter in my web page. But I also need to send email immediately. So I have used this solution
If I send newsletter with Spool everything is fine. But when I use
$mailer = $this->get('instant_mailer');
I receive email with some text prepend at the beginning:
HTTP/1.0 200 OK Cache-Control: no-cache Content-Type: text/html; charset=UTF-8 Date: Fri, 07 Sep 2012 16:19:06 GMT
How to remove this?
I bet that you're trying to send a Response object.
new Response();
it goes to __toString ()
public function __toString()
{
$this->prepare();
return
sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
$this->headers."\r\n".
$this->getContent();
}
It is because:
$this->render('template.html.twig');
returns Response to avoid that use:
$response = $this->render('template.html.twig');
$text = $response->getContent();
Regards,
Max
Use
$content = $this->renderView('template.html.twig');
instead of
$content = $this->render('template.html.twig');
render returns a response
Other posible solution to the problem is to use templating service instead of $this->render():
<?php
$body = $this->get('templating')->render('template.html.twig');

Why this json Symfony output outputs the headers

This is my first day to have fun with Symfony and drupal 8, so please excuse me if my question is very obvious.
With drupal 7:
drupal_json_output(array('products' => array_values($products)));
exit;
the json output is clean:
{"products":["item_1","item_2",....]}
With drupal 8:
use Symfony\Component\HttpFoundation\JsonResponse;
// some process
print new JsonResponse(array('products' => array_values($products)));
exit;
It outputs with the headers:
HTTP/1.0 200 OK
Cache-Control: no-cache
Content-Type: application/json
Date: Wed, 18 Jul 2012 07:53:26 GMT
{"products":["item_1","item_2",....]}
How do you get rid of those headers?
I am stuck to read the reference here.
Any hint is very much appreciated.
You can get only the "content" of a response by calling $response->getContent().
In your case you could do
use Symfony\Component\HttpFoundation\JsonResponse;
// some process
$response = new JsonResponse(array('products' => array_values($products)));
print $response->getContent();
exit;
However, be aware that this would be a bad practice because you would lose the response headers in the process, and wouldn't tell for example, what the content-type of you response is (in this case: "application/json") etc ...
I do not know how to do this properly with drupal, any tips is appreciated.

Resources