Robert, 

I incorporated your changes into my code and pushed to production. I'll chime 
back and see if this solves the issue. 

Just for completeness, your suggestion to do:

exception = Exception.new("Expecting to have order, but session doesn't have 
one")
ExceptionNotifier::Notifier.exception_notification(request.env, 
exception).deliver

Doesn't work because if the exception is not raised it doesn't contain the 
backtrace which ExceptionNotifier expects to output, so I still need to raise 
and rescue. 

Thanks again for chiming in. 

-- 
Ylan Segal
[email protected]
Tel: +1-858-224-7421
Fax: +1-858-876-1799

On Dec 29, 2011, at 12:11 PM, Robert Kaufman wrote:

> Hi Ylan,
>   
>   Session store can be a pain in the butt, especially if users have different 
> browser settings.  Checking that it is not a browser version thing is a good 
> start, but I wonder if you've ever been able to reproduce the issue with a 
> computer that you control?  Sometimes toolbars (Yahoo, Norton, etc) or 
> security software (Anti Virus, Firewall, etc) or browser preferences can mess 
> up your session storage.  I'm assuming from your code that the order id's are 
> fairly short numbers, so you probably are not over running the session size 
> limits.
> 
> 
>   Setting browser issues aside for a moment, I do see a few potential problem 
> spots in your code, which might account for some edge case behavior.  Lets 
> try cleaning those up and then see where we are.  I made you a series of 
> Gists, the first one is your code, but with a bunch of comments about what 
> I'm changing and why:
> 
> https://gist.github.com/4627ab3dcf02ad1f7da2/eb4d288a8153ddab40ff466d88613a50082c18ea
> 
> This second one has the actual changes that I would make.  I've done all of 
> this just in a text editor, so there may be typos, but it should show my 
> thought process at the moment.
> 
> https://gist.github.com/4627ab3dcf02ad1f7da2/10b70fc8a516aa45bad80d84a3a47fec388add13
> 
> 
> Best,
> Rob Kaufman
> 
> 
> On Dec 29, 2011, at 11:02 , Ylan Segal wrote:
> 
>> I think I didn't make myself clear: If I remove the block, I don't get the 
>> same exception: I get 
>> 
>> ActionView::Template::Error: undefined method `model_name' for NilClass:Class
>> 
>> Which (eventually) traces back to the session[order_id] being nil. 
>> 
>> The begin rescue thing is just so that ExceptionNotifier emails me the 
>> environment information. It is being raised immediately in the preceding 
>> line. It's just a quick and dirty hack so that I am getting as much info as 
>> I can on this. The important part is that session[order_id] is not expected 
>> to be nil, but it is. 
>> 
>> I hope that clears it up.
>> 
>> --
>> Ylan Segal
>> [email protected]
>> 
>> On Dec 29, 2011, at 10:31 AM, Ben Wanicur wrote:
>> 
>>> I'm surprised that if you removed the exception block, that you still get 
>>> your exception message (" Expecting to have order, but session doesn't have 
>>> one").  Maybe I am not understanding you.
>>> 
>>> Anyways, here is what I mean about narrowing your exception handling.  This 
>>> might not be your problem, but it's a good alternative to rescuing every 
>>> exception:
>>> 
>>> class HomeGrownError < StandardError
>>> end
>>> 
>>> ...
>>> ...
>>> ...
>>> 
>>> protected
>>> # This is where I am caching the exceptions.
>>> def check_for_existing_order
>>>   unless current_order?
>>>     begin
>>>       raise HomeGrownError("Expecting to have order, but session doesn't 
>>> have one")
>>>     rescue HomeGrownError => exception
>>>       ExceptionNotifier::Notifier.exception_notification(request.env, 
>>> exception).deliver
>>>     ensure
>>>       redirect_to root_path unless Rails.env.development?
>>>       return
>>>     end
>>>   end
>>> end
>>> 
>>> 
>>> On Thu, Dec 29, 2011 at 10:13 AM, Ylan Segal <[email protected]> wrote:
>>> Ben,
>>> 
>>> def current_order?
>>>  session[:order_id]
>>> end
>>> 
>>> Since the problem only happens on production, I wasn't really getting full 
>>> stack trace information if I let the exception through. That is why I added 
>>> the rescue block in the first place: It sends me all the info with 
>>> ExceptionNotifier. Without the rescue block, eventually what happens is a 
>>> call a property that is nil on the order and that throws an exception, but 
>>> the problem is that the order_id is nill in the first place and it 
>>> shouldn't be.
>>> 
>>> Here is what that output looks like:
>>> 
>>> A RuntimeError occurred in orders#show:
>>> 
>>> Expecting to have order, but session doesn't have one
>>> app/controllers/lbd/orders_controller.rb:124:in `check_for_existing_order'
>>> 
>>> -------------------------------
>>> Request:
>>> -------------------------------
>>> 
>>> * URL       : http://www.edited.com/order
>>> * IP address: 184.73.92.227
>>> * Parameters: {"action"=>"show", "controller"=>"lbd/orders"}
>>> * Rails root: /app
>>> 
>>> -------------------------------
>>> Session:
>>> -------------------------------
>>> 
>>> * session id: "8913455d4a9e22b76d48c438f90aaea6"
>>> * data: {"session_id"=>"8913455d4a9e22b76d48c438f90aaea6"}
>>> 
>>> 
>>> -------------------------------
>>> Environment:
>>> -------------------------------
>>> 
>>> * GATEWAY_INTERFACE                              : CGI/1.2
>>> * HTTP_ACCEPT                                    : */*
>>> * HTTP_ACCEPT_ENCODING                           : gzip
>>> * HTTP_CONNECTION                                : close
>>> * HTTP_HOST                                      : www.edited.com
>>> * HTTP_USER_AGENT                                : Mozilla/5.0 (Windows NT 
>>> 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1 SmartLinksAddon
>>> * HTTP_VERSION                                   : HTTP/1.1
>>> * HTTP_VIA                                       : 1.1 
>>> web02.mashlogic.local:3028 (squid/2.7.STABLE9)
>>> * HTTP_X_FORWARDED_FOR                           : 127.0.0.1, 50.17.249.10, 
>>> 10.114.118.95
>>> * HTTP_X_FORWARDED_PORT                          : 80
>>> * HTTP_X_FORWARDED_PROTO                         : http
>>> * HTTP_X_HEROKU_DYNOS_IN_USE                     : 1
>>> * HTTP_X_HEROKU_QUEUE_DEPTH                      : 0
>>> * HTTP_X_HEROKU_QUEUE_WAIT_TIME                  : 3
>>> * HTTP_X_REAL_IP                                 : 10.114.118.95
>>> * HTTP_X_REQUEST_START                           : 1325180702283
>>> * HTTP_X_VARNISH                                 : 2592570555
>>> * PATH_INFO                                      : /order
>>> * QUERY_STRING                                   :
>>> * REMOTE_ADDR                                    : 10.66.162.3
>>> * REQUEST_METHOD                                 : GET
>>> * REQUEST_PATH                                   : /order
>>> * REQUEST_URI                                    : /order
>>> * SCRIPT_NAME                                    :
>>> * SERVER_NAME                                    : www.edited.com
>>> * SERVER_PORT                                    : 80
>>> * SERVER_PROTOCOL                                : HTTP/1.1
>>> * SERVER_SOFTWARE                                : thin 1.2.6 codename 
>>> Crazy Delicious
>>> * action_controller.instance                     : orders#show
>>> * action_dispatch.cookies                        : 
>>> #<ActionDispatch::Cookies::CookieJar:0x0000000b4e3c38>
>>> * action_dispatch.parameter_filter               : [:password, 
>>> :credit_card, :payment_profile, /RAW_POST_DATA/, /RAW_POST_DATA/]
>>> * action_dispatch.remote_ip                      : 50.17.249.10
>>> * action_dispatch.request.accepts                : [*/*]
>>> * action_dispatch.request.content_type           :
>>> * action_dispatch.request.formats                : [*/*]
>>> * action_dispatch.request.parameters             : {"action"=>"show", 
>>> "controller"=>"lbd/orders"}
>>> * action_dispatch.request.path_parameters        : {:action=>"show", 
>>> :controller=>"lbd/orders"}
>>> * action_dispatch.request.query_parameters       : {}
>>> * action_dispatch.request.request_parameters     : {}
>>> * action_dispatch.request.unsigned_session_cookie: 
>>> {"session_id"=>"018022ab5db5fe76684e2d1a5867d8a8"}
>>> * action_dispatch.routes                         : 
>>> #<ActionDispatch::Routing::RouteSet:0x0000000496b690>
>>> * action_dispatch.secret_token                   : 
>>> 0d72ef374495f4ed8ae2bd664e0fbf43e8c4f1a71cef4fb11d23dcc19dd55d0bbfe3fb9c55e59b8fe509c5d2c3ca77976f23c5f39392121e8d77d8b68de4359c
>>> * action_dispatch.show_exceptions                : true
>>> * async.callback                                 : #<Method: 
>>> Thin::Connection#post_process>
>>> * async.close                                    : 
>>> #<EventMachine::DefaultDeferrable:0x0000000b502660>
>>> * rack-cache.allow_reload                        : false
>>> * rack-cache.allow_revalidate                    : false
>>> * rack-cache.cache_key                           : Rack::Cache::Key
>>> * rack-cache.default_ttl                         : 0
>>> * rack-cache.entitystore                         : rails:/
>>> * rack-cache.metastore                           : rails:/
>>> * rack-cache.private_headers                     : ["Authorization", 
>>> "Cookie"]
>>> * rack-cache.storage                             : 
>>> #<Rack::Cache::Storage:0x00000003c61918>
>>> * rack-cache.use_native_ttl                      : false
>>> * rack-cache.verbose                             : true
>>> * rack.errors                                    : #<IO:0x00000000b39890>
>>> * rack.input                                     : 
>>> #<StringIO:0x0000000b503038>
>>> * rack.multiprocess                              : false
>>> * rack.multithread                               : false
>>> * rack.request.cookie_hash                       : {}
>>> * rack.request.query_hash                        : {}
>>> * rack.request.query_string                      :
>>> * rack.run_once                                  : false
>>> * rack.session                                   : 
>>> {"session_id"=>"018022ab5db5fe76684e2d1a5867d8a8", 
>>> "abingo_identity"=>9991086208}
>>> * rack.session.options                           : {:key=>"_locum_session", 
>>> :path=>"/", :domain=>nil, :expire_after=>nil, :secure=>false, 
>>> :httponly=>true, :defer=>false, :renew=>false, 
>>> :coder=>#<Rack::Session::Cookie::Base64::Marshal:0x000000058583b0>, 
>>> :id=>"018022ab5db5fe76684e2d1a5867d8a8"}
>>> * rack.url_scheme                                : http
>>> * rack.version                                   : [1, 0]
>>> * warden                                         : Warden::Proxy:94837780 
>>> @config={:default_scope=>:user, :scope_defaults=>{}, 
>>> :default_strategies=>{:user=>[:database_authenticatable]}, 
>>> :intercept_401=>false, :failure_app=>#<Devise::Delegator:0x00000005d371b0>}
>>> 
>>> * Process: 1
>>> * Server : 4950a26f-9aa2-4265-af61-f08d70fc8a30
>>> 
>>> --
>>> Ylan Segal
>>> [email protected]
>>> 
>>> 
>>> On Dec 29, 2011, at 10:00 AM, Ben Wanicur wrote:
>>> 
>>>> Sorry, I missed your def current_order? method (formatting is really bad 
>>>> on my computer here).... I would remove the exception handling (you are 
>>>> capturing everything with your rescue block), and see what more 
>>>> information you can gather.
>>>> 
>>>> 
>>>> 
>>>> On Thu, Dec 29, 2011 at 9:43 AM, Ylan Segal <[email protected]> wrote:
>>>> Sure. Here we go:
>>>> 
>>>> #config/session_store.rb
>>>> Locum::Application.config.session_store :cookie_store, :key => 
>>>> '_locum_session'
>>>> 
>>>> #ApplicationController.rb
>>>> def current_order
>>>>   if session[:order_id]
>>>>     current_order = Order.find(session[:order_id])
>>>>   end
>>>>   if session[:order_id].nil?
>>>>     current_order = Order.new
>>>>   end
>>>>   current_order
>>>> end
>>>> 
>>>> def current_order?
>>>>   session[:order_id]
>>>> end
>>>> 
>>>> #OrdersController (inherits from Application Controller)
>>>> before_filter :check_for_existing_order, :except => :except => [:add_item]
>>>> 
>>>> # Users cannot see any other action on the app until they have gone 
>>>> through add_item.
>>>> def add_item
>>>>   @order = current_order
>>>>   @order.save
>>>>   session[:order_id] = @order.id
>>>>   variant = Variant.where(:sku => params[:variant_sku]).first
>>>>   quantity = params[:quantity].to_i
>>>>   @order.add_item(variant, quantity) unless variant.nil?
>>>>   redirect_to order_path
>>>> end
>>>> 
>>>> def show
>>>>   @order = current_order
>>>> end
>>>> 
>>>> def update
>>>>   @order = current_order
>>>>   @order.attributes = params[:order]
>>>>   if @order.save
>>>>     flash[:notice] = 'Your cart has been updated successfully'
>>>>   else
>>>>     flash[:alert] = "Your cart could not be updated: 
>>>> #{@order.errors.full_messages.join(',')}"
>>>>   end
>>>>   redirect_to order_path
>>>> end
>>>> 
>>>> protected
>>>> # This is where I am caching the exceptions.
>>>> def check_for_existing_order
>>>>   unless current_order?
>>>>     begin
>>>>       raise "Expecting to have order, but session doesn't have one"
>>>>     rescue => exception
>>>>       ExceptionNotifier::Notifier.exception_notification(request.env, 
>>>> exception).deliver
>>>>     ensure
>>>>       redirect_to root_path unless Rails.env.development?
>>>>       return
>>>>     end
>>>>   end
>>>> end
>>>> 
>>>> #config/routes.rb
>>>> resource :order, :except => [:destroy] do
>>>>     member do
>>>>       get :checkout
>>>>       post :add_item
>>>>     end
>>>>   end
>>>> 
>>>> I added the important parts (I think). Thanks for the help!
>>>> 
>>>> --
>>>> Ylan Segal
>>>> [email protected]
>>>> 
>>>> On Dec 29, 2011, at 9:24 AM, Ben Wanicur wrote:
>>>> 
>>>>> Can you post your code Ylan ?  Also, can you include and session store 
>>>>> config files as well ?
>>>>> 
>>>>> On Thu, Dec 29, 2011 at 9:21 AM, Ylan Segal <[email protected]> wrote:
>>>>> Fellow Rubyists,
>>>>> 
>>>>> I have been scratching my head on this one for a while: I have an 
>>>>> e-commerce app in production with pretty standard add-to-cart 
>>>>> functionality. Whenever a user starts an order, I add the order id to the 
>>>>> session and read it back on subsequent requests (like showing the cart, 
>>>>> checkout form, etc). By and large, this works as expected in my tests and 
>>>>> testing with local browsers.
>>>>> 
>>>>> However, for a small number of my users, the session doesn't seems to 
>>>>> keep the order id. In actions where I would expect the order id to be 
>>>>> there, it is not. For example, I have a OrdersController#update action 
>>>>> that, well, updates the order. Following convention, that actions 
>>>>> forwards to OrdersController#show. Since the session[:order_id] was 
>>>>> present in #update, I would obviously expect it to be set in #show, and 
>>>>> for most users it is, but for some it is not. I am obviously, checking 
>>>>> that the user is not visiting #show before #update or #create has been 
>>>>> called.
>>>>> 
>>>>> Random stuff that I think might make a difference
>>>>> 
>>>>> * It is not restricted to any one browser/version. It happens with IE, 
>>>>> Firefox, etc.
>>>>> * It happens with browsers that have cookies (I can see when analyzing 
>>>>> requests, that the cookies headers are being sent, it just that the 
>>>>> session doesn't contain the expected variable/value).
>>>>> * Running Rails 3.1 on heroku with default session store.
>>>>> 
>>>>> Currently, my only hunch is that the users browser is not sending the 
>>>>> latest version of a cookie (with the order_id set in the session) but the 
>>>>> original one generated when the first visited the site (where the 
>>>>> session[:order_id] has not been set).
>>>>> 
>>>>> Has anyone experiences something like this? Would using another kind of 
>>>>> cookie store solve this?
>>>>> 
>>>>> Thanks for the help,
>>>>> 
>>>>> --
>>>>> Ylan Segal
>>>>> [email protected]
>>>>> Tel: +1-858-224-7421
>>>>> Fax: +1-858-876-1799
>>>>> 
>>>>> --
>>>>> SD Ruby mailing list
>>>>> [email protected]
>>>>> http://groups.google.com/group/sdruby
>>>>> 
>>>>> 
>>>>> --
>>>>> SD Ruby mailing list
>>>>> [email protected]
>>>>> http://groups.google.com/group/sdruby
>>>> 
>>>> --
>>>> SD Ruby mailing list
>>>> [email protected]
>>>> http://groups.google.com/group/sdruby
>>>> 
>>>> 
>>>> --
>>>> SD Ruby mailing list
>>>> [email protected]
>>>> http://groups.google.com/group/sdruby
>>> 
>>> --
>>> SD Ruby mailing list
>>> [email protected]
>>> http://groups.google.com/group/sdruby
>>> 
>>> 
>>> -- 
>>> SD Ruby mailing list
>>> [email protected]
>>> http://groups.google.com/group/sdruby
>> 
>> -- 
>> SD Ruby mailing list
>> [email protected]
>> http://groups.google.com/group/sdruby
> 
> 
> -- 
> SD Ruby mailing list
> [email protected]
> http://groups.google.com/group/sdruby

-- 
SD Ruby mailing list
[email protected]
http://groups.google.com/group/sdruby

Reply via email to