This is a Rack application:
run -> (env) do
[200, {}, ["hello friend"]]
end
This is a Rack application using sessions:
use Rack::Session::Pool, secret: SecureRandom.uuid
run -> (env) do
count = env["rack.session"][:count] ||= 1
env["rack.session"][:count] += 1
[
200,
{},
[
(["hello friend"] * count).join(", ")
]
]
end
Rack::Session::Pool
isn’t the only option. You can use Rack::Session::Cookie
instead.
Be aware! Rack::Session::Pool
stores sessions in an instance variable. Therefore, it resets if you restart the server, it won’t work by default if you have a load balancer, and similar situations.
Rack::Session::Cookie
does marshalling and store sessions in cookies, so you will have persistent sessions at the price of marshalling. However, marshaling has it’s performance costs, and you lost flexibility with what you can put in sessions.¹ Also, storing in cookies means you will increase the amount of data in your requests.
If you decide to use Rack::Session::Pool
in your application, be cautious with how you include the middleware in your application. The following application wouldn’t work, for example:
app = Rack::Builder.new do
use Rack::Session::Pool, secret: SecureRandom.uuid
run -> (env) do
count = env["rack.session"][:count] ||= 1
env["rack.session"][:count] += 1
[
200,
{},
[
(["hello friend"] * count).join(", ")
]
]
end
end
run app
The code ends up calling to_app
on app
for every request, which causes a new instance of Rack::Session::Pool
for every request - since it tracks sessions by an instance variable it won’t be able to track sessions: just calling Rack::Builder.app
instead of Rack::Builder.new
would make it work:
From lib/rack/builder.rb:118:
# Create a new Rack::Builder instance and return the Rack application
# generated from it.
def self.app(default_app = nil, &block)
self.new(default_app, &block).to_app
end