I'm storing the Rails Sessions in database using Active Record Store. At some point I want to replace the actual Rails Session for another one extracted from the database.
Let's say the Session to restore ID comes in the param session_id.
How can I retrieve the Session to restore and replace the actual session?
The simplest way I found to do it is in a before_filter
def restore_session
return if params[:session_id].blank?
restored_session = ActiveRecord::SessionStore::Session.find_by_session_id(params[:session_id])
if restored_session
session.update(restored_session.data)
restored_session.destroy
end
end
Related
What happens if I use transactions for write operations but don't use those for read operations?
My use case:
get some data1 from db (without transaction)
create some data2 using data1 (with transaction)
get some data3 from db (without transaction)
create some data4 using data2 and data3 (with transaction)
If no error commit otherwise rollback.
Is it something wrong that I am not using transaction for the 2 read queries?
Edit/Add/Delete Records
A Transaction is used when you want to ensure that a bunch of row edit/add/delete queries are committed together to the db. In other wards, you want to ensure that all sql commands in that bunch runs successfully or don't commit any of the commands. E.g. you are saving a new record for a users table and a users address table together, but you might not want to write to the users table if the address table record fails for some reason. In this case you would use a transaction for both commands.
Read Records
If you understand the above, you know you don't need transactions for read sql commands.
Was the answer helpful? Consider marking the answer tick and upvoting. Thanks 🙏
If that sequence is fine or not depends on your requirements. With your current procedure, the following could happen:
if you encounter an error before step 2 finishes, nothing has changed
if you encounter an error before step 5 finishes, you have only data2, but not data4
if no error happens before step 5 has completed, you have data2 and data4
If that is fine for you, there is no problem with what you are doing.
So if you're going to query the database for the same rows that you just inserted using a transaction, but haven't committed the transaction yet, then you should read from the database using the transaction.
Eg. You create a user, then you need to create an external account for this user, and the method that creates that external account reads the user from the database and does not get it as a parameter. You can either modify the create external account method so it gets the user as a parameter and then pass to it the just created user, either you can keep the method like it is, but you have to make sure you pass the transaction to it. Otherwise, if the transaction is not committed and is not passed to the read query, the created user won't be found.
Ideally you should avoid this thing by passing the input data to the create external account too, so you don't need to read the user from db, but if for some reason this is not possible, then make sure you read from the db using the transaction.
I've got few counter caches which update when model save/update or destroy because of after hook. If I create/update or destroy record using raw SQL, how can I update counter cache automatically?
This should help you
http://apidock.com/rails/ActiveRecord/CounterCache/ClassMethods/reset_counters
# For Post with id #1 records reset the comments_count
Post.reset_counters(1, :comments)
Resets one or more counter caches to their correct value using an SQL count query. This is useful when adding new counter caches, or if the counter has been corrupted or modified directly by SQL.
I have a model that loads data directly to the database with the pg gem. It does this by creating a CSV file on the fly, and assigning the values to the id field by calling:
self.class.select("nextval('apps_id_seq'::regclass)").first['nextval']
in a to_csv instance method.
This works great in production. However, I'm in the process of filling in some tests, and if this test is run first, or by itself, it fails because the DB has just been reset by the Database Cleaner gem and the sequences are reset. But they aren't given a starting value.
Postgres documentation says you can call setval on the sequence in question to set it up. So let's say I want to set it up to start at 1, so that the next time nextval is called, it will return a 1:
select setval('apps_id_seq'::regclass, 1, false);
But I can't call this the same way as I call nextval above, because it just plain doesn't work. It returns nil, and does not set up the sequence.
No iteration of anything I try:
self.class.select("setval('apps_id_seq'::regclass)").first
App.connection.execute("select setval('apps_id_seq'::regclass,1,false)")
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1)")
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1,false)")
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1,false)")
nor any combination thereof works, it just refuses to work. I don't know if it's a pg gem problem.
UPDATE
These statements work in development mode:
App.select("setval('apps_id_seq'::regclass,1)").first
and:
App.select("nextval('apps_id_seq'::regclass)").first
Yet, neither of them work in test.
BUT, these statements DO work in test mode:
ActiveRecord::Base.connection.execute("select setval('apps_id_seq'::regclass,1,false)").first
and:
ActiveRecord::Base.connection.execute("select nextval('apps_id_seq'::regclass)").first
The ONLY DIFFERENCE in the two environments, as far as I can see, is that one has data, while the other one does not. And my test db is created with rake db:test:prepare from a structure.sql file.
setval and nextval will work regardless of whether there is data in the table that the sequence is attached to. A sequence can be created and incremented without even being associated to a column of an existing table. When you use Model.select to execute the raw SQL, ActiveRecord generates the SQL with a from clause for the table of the model you are using:
> App.select("setval('apps_id_seq'::regclass,1)").first
SELECT nextval('apps_id_seq'::regclass)
FROM "apps" ORDER BY "apps"."id" ASC LIMIT 1'
=> nil
Because there are no records in the apps table, the setval function is never executed. This is why the same select works when using the connection directly because the select doesn't have the extraneous from clause:
> ActiveRecord::Base.connection.execute("SELECT setval('apps_id_seq'::regclass, 1)").first
SELECT setval('apps_id_seq'::regclass, 1)
=> {"setval"=>"1"}
Use this form to execute SQL so that you can ensure the setval function is always evaluated.
I've created a test case to persist data to my db. The fixtures persist correctly but the records I create in the test case aren't showing up in the database. My test looks like the following:
test "new course" do
course = Course.new
course.name = 'test'
assert course.save
end
The test passes and when I debug through it and call course.save myself I can see that it has a generated ID. Any ideas?
If you're attempting to check that the data is in the database after the test, this won't be the case due to how the tests are run.
Tests for Rails are run inside database transactions (by default1) and so it will go ahead and create the data inside the test and run all your assertions, but once it's all said and done it's going to rollback that transaction leaving the database in a pristine state.
The reason for doing this is so that the database is always clean and so that you don't have records just lying about which could potentially alter the outcome of tests.
1 Although this can be changed by disabling the setting (I forget what this is in Test::Unit land), or by using tools like database_cleaner.
I'm trying to find out if a specific MySQL User is still in use in our system (and what queries it is executing).
So I thought of writing a trigger that would kick in anytime user X executes a query, and it would log the query in a log table.
How can I do that?
I know how to write a query for a specific table, but not for a specific user (any table).
Thanks
You could branch your trigger function on USER().
The easiest would be to have the trigger always fire, but only logs if the user is X.
I would look at these options:
A) Write an audit plugin, which filters events based on the user name.
For simplicity, the user name can be hard coded in the plugin itself,
or for elegance, it can be configured by a plugin variable, in case this problem happens again.
See
http://dev.mysql.com/doc/refman/5.5/en/writing-audit-plugins.html
B) Investigate the --init-connect server option.
For example, call a stored procedure, check the value of user() / current_user(),
and write a trace to a log (insert into a table) if a connection from the user was seen.
See
http://dev.mysql.com/doc/refman/5.5/en/server-system-variables.html#sysvar_init_connect
This is probably the closest thing to a connect trigger.
C) Use the performance schema instrumentation.
This assumes 5.6.
Use table performance_schema.setup_instrument to only enable the statement instrumentation.
Use table performance_schema.setup_actors to only instrument sessions for this user.
Then, after the system has been running for a while, look at activity for this user in the following tables:
table performance_schema.users will tell if there was some activity at all
table performance_schema.events_statements_history_long will show the last queries executed
table performance_schema.events_statements_summary_by_user will show aggregate statistics about each statement types (SELECT, INSERT, ...) executed by this user.
Assuming you have a user defined as 'old_app'#'%', a likely follow up question will be to find out where (which host(s)) this old application is still connecting from.
performance_schema.accounts will just show that: if traffic for this user is seen, it will show each username # hostname source of traffic.
There are statistics aggregated by account also, look for '%_by_account%' tables.
See
http://dev.mysql.com/doc/refman/5.6/en/performance-schema.html
There are also other ways you could approach this problem, for example using MySQL proxy
In the proxy you could do interesting things - from logging to transforming queries, pattern matching (check this link also for details on how to test/develop the scripts)
-- set the username
local log_user = 'username'
function read_query( packet )
if proxy.connection.client.username == log_user and string.byte(packet) == proxy.COM_QUERY then
local log_file = '/var/log/mysql-proxy/mysql-' .. log_user .. '.log'
local fh = io.open(log_file, "a+")
local query = string.sub(packet, 2)
fh:write( string.format("%s %6d -- %s \n",
os.date('%Y-%m-%d %H:%M:%S'),
proxy.connection.server["thread_id"],
query))
fh:flush()
end
end
The above has been tested and it does what it is supposed to (although this is a simple variant, does not log success or failure and only logs proxy.COM_QUERY, see the list of all constants to see what is skipped and adjust for your needs)
Yeah, fire away, but use whatever system you have to see what user it is (cookies, session) to log only if the specific user (userID, class) matches your credentials.