module VarkLogging
# any constants you want to define
VARK_LOG_MESSAGE = "vark log!"
def self.included(base)
base.send(:include, InstanceMethods)
base.send(:extend, ClassMethods)
base.class_eval do
# any class methods we need to call
alias_method_chain :add, :vark_logging
end
end
module InstanceMethods
def add_with_vark_logging(*args)
puts VARK_LOG_MESSAGE
add_without_vark_logging(*args)
end
end
module ClassMethods
def vark?
true
end
end
end
# and if you want to include it without altering the original class definition:
Logger.send(:include, VarkLogging)
Friday, January 30, 2009
nice module pattern
This is a really nice pattern for mixins. It allows for clear separation of instance and class methods and allows you to cleanly specify constants and call any class methods you need.
Mounting extra disks on ec2 instances
First, compare available partitions with
We want to check the fstab to see what the disktypes are. Also edit this file if you want your mounted disk to come back up on a reboot! (In Ec2-land all of these filesystems are non-persistent, so if your machine more than reboots, you'll lose the data entirely.
~> cat /proc/partitionsHere we note that /dev/sda1 and /dev/sdb are in use, while /dev/sdc /dev/sdd and /dev/sde are not yet in use
major minor #blocks name
8 16 440366080 sdb
8 32 440366080 sdc
8 48 440366080 sdd
8 64 440366080 sde
8 1 10485760 sda1
~> df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 9.9G 1.6G 7.9G 17% /
/dev/sdb 414G 199M 393G 1% /mnt
none 3.6G 0 3.6G 0% /dev/shm
We want to check the fstab to see what the disktypes are. Also edit this file if you want your mounted disk to come back up on a reboot! (In Ec2-land all of these filesystems are non-persistent, so if your machine more than reboots, you'll lose the data entirely.
~> more /etc/fstabCreate your mount point:
/dev/sda1 / ext3 defaults 1 1
/dev/sdb /mnt ext3 defaults 0 0
none /dev/pts devpts gid=5,mode=620 0 0
none /dev/shm tmpfs defaults 0 0
none /proc proc defaults 0 0
none /sys sysfs defaults 0 0
~> mkdir /mnt2Mount the disk:
~> mount -t ext3 /dev/sdc /mnt2And check its availability:
~> df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda1 9.9G 1.6G 7.9G 17% /
/dev/sdb 414G 199M 393G 1% /mnt
none 3.6G 0 3.6G 0% /dev/shm
/dev/sdc 414G 199M 393G 1% /mnt2
Dealing with Capistrano's cached-copy when switching the branch you're releasing from
Capistrano's default setup tries to deploy from a repository location you specify, and does so by updating a cached-copy on the remote servers using "svn up", and then recursively copying the updated cached checkout to a date-tagged copy under the releases directory.
e.g., when capistrano executes
So, before you release from a tag, you have to remember to delete the cached copy checked out on the servers. Capistrano's invoke comes in handy for these situations:
e.g., when capistrano executes
~> cap deploy:updateit runs something like the following:
executing “if [ -d /deploy/myproject/shared/cached-copy ]; then svn update -q -r3131 /deploy/myproject/shared/cached-copy; else svn checkout -q -r3131 https://svn.example.com/svn/myreponame/tags/release-1.2.1/myproject /deploy/myproject/shared/cached-copy; fi”This can cause some problems when you try to switch the repository branch you're deploying from, say, to roll back to a tag, or deploy from a radically different stable branch where svn up won't run properly because of deleted directories etc.
So, before you release from a tag, you have to remember to delete the cached copy checked out on the servers. Capistrano's invoke comes in handy for these situations:
~> cap invoke COMMAND="rm -rf /deploy/myproject/shared/cached-copy"You might want to also remember to delete the cached-copy *after* you deploy too, in case the next person who comes along doesn't realize that you deployed from a strange location.
i can't believe it's this hard to set default values at the AR object level (not the db level)
This is what I had to do:
Really?
before_save :maybe_set_source
def maybe_set_source
self[:source] = source_name if !self[:source]
end
def source
self[:source] || source_name
end
# The default value I want to set
def source_name
File.basename(RAILS_ROOT)
end
Really?
Thursday, January 29, 2009
assert_yields
I got sick of writing longhand assertions around yields so I whipped up this little function:
One could easily make it handle a variable number of arguments.
def assert_yields(something_callable, arg)
yielded = false
something_callable.call(arg) do
yielded = true
end
assert yielded
end
# Called like:
assert_yields(some_obj.method(:do_something), my_arg)
One could easily make it handle a variable number of arguments.
Object is not missing constant Question!
when Rails says, "Object is not missing constant Question!" -- here's what happened:
You referenced a class that wasn't defined, so ActiveSupport's dependency resolver kicked in.
It loaded the appropriate file (in this case question.rb), but during the load an Exception was raised. The error was not, however, in your class definition, so the missing constant (Question) was successfully defined.
The resolver then catches that exception, expects that your class will not be defined but finds that it is, and decides to throw an "Object is not missing constant Question!" because it's confused.
You referenced a class that wasn't defined, so ActiveSupport's dependency resolver kicked in.
It loaded the appropriate file (in this case question.rb), but during the load an Exception was raised. The error was not, however, in your class definition, so the missing constant (Question) was successfully defined.
The resolver then catches that exception, expects that your class will not be defined but finds that it is, and decides to throw an "Object is not missing constant Question!" because it's confused.
tell it, joel
On how not to write a resume for a startup engineering leadership
position (and, by implication, what to look for):
position (and, by implication, what to look for):
http://www.joelonsoftware.com/items/2009/01/02b.html
--
Love,
Fritz
Wednesday, January 28, 2009
overriding string comparisons
Monkeypatching = Bad. Aliasing = Not Bad. See r7311. It blew up
because Question was also overriding String#==.
because Question was also overriding String#==.
--
Love,
Fritz
this should come as no surprise
but don't override AR initialize():
http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html
In fact, don't override anything in AR :)
--
Love,
Fritz
http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html
In fact, don't override anything in AR :)
--
Love,
Fritz
Friday, January 23, 2009
chill that Dock out!
How to keep icons in your mac os dock from performing their g-darned,
vatter-effing bouncing when they want your attention:
vatter-effing bouncing when they want your attention:
http://www.macworld.com/article/138403/2009/01/dockbounce.html?lsrc=rss_weblogs_macosxhints
--
Love,
Fritz
Thursday, January 22, 2009
great splunk howtos
using a simple tcp logger implementation:
http://www.igvita.com/2008/06/19/splunk-your-distributed-logs-in-ec2/
using syslog-ng:
http://www.igvita.com/2008/10/22/distributed-logging-syslog-ng-splunk/
http://www.igvita.com/2008/06/19/splunk-your-distributed-logs-in-ec2/
using syslog-ng:
http://www.igvita.com/2008/10/22/distributed-logging-syslog-ng-splunk/
AR constructs chained named scope queries in the order opposite what you'd expect
Here's a good one. Consider this chain of named scopes:
Conversation.in_the_last(1.day).
with_question.
not_error.
not_canceled.
not_flagged.
not_answered.
not_involving(user)
You'd expect the created_at restriction to come first in the resulting SELECT, wouldn't you? Nope:
SELECT * FROM `conversations` WHERE (((((((NOT EXISTS (SELECT chs3.id FROM channels chs3 WHERE conversations.id = chs3.conversation_id AND chs3.user_id = 176)) AND (NOT EXISTS (SELECT chs2.id FROM channels chs2 WHERE conversations.id = chs2.conversation_id AND chs2.answer_request_response = 'answer'))) AND (NOT EXISTS (SELECT chs1.id FROM channels chs1 WHERE conversations.id = chs1.conversation_id AND chs1.flagged = 1))) AND (`conversations`.`canceled` = 0 )) AND (`conversations`.`error` = 0 )) AND (EXISTS (SELECT chs4.id FROM channels chs4 WHERE conversations.id = chs4.conversation_id AND chs4.asker = 1 AND chs4.has_question = 1))) AND (created_at > '2009-01-21 16:38:23'));
AR sticks it at the end of the query and as a result it runs in 6-10 seconds on acceptance (w/no query cache). Applying the created_at restriction at the end of the chain constructs a query with it at the beginning:
Conversation.with_question.
not_error.
not_canceled.
not_flagged.
not_answered.
not_involving(user).
in_the_last(1.day)
SELECT * FROM `conversations` WHERE (((((((created_at > '2009-01-21 18:07:29') AND (NOT EXISTS (SELECT chs3.id FROM channels chs3 WHERE conversations.id = chs3.conversation_id AND chs3.user_id = 176))) AND (NOT EXISTS (SELECT chs2.id FROM channels chs2 WHERE conversations.id = chs2.conversation_id AND chs2.answer_request_response = 'answer'))) AND (NOT EXISTS (SELECT chs1.id FROM channels chs1 WHERE conversations.id = chs1.conversation_id AND chs1.flagged = 1))) AND (`conversations`.`canceled` = 0 )) AND (`conversations`.`error` = 0 )) AND (EXISTS (SELECT chs4.id FROM channels chs4 WHERE conversations.id = chs4.conversation_id AND chs4.asker = 1 AND chs4.has_question = 1)))
Apparently it can now use the created_at to eliminate rows in the scan. The resulting query time is about 300ms on acceptance (again, w/no query cache).
Conversation.in_the_last(1.day).
with_question.
not_error.
not_canceled.
not_flagged.
not_answered.
not_involving(user)
You'd expect the created_at restriction to come first in the resulting SELECT, wouldn't you? Nope:
SELECT * FROM `conversations` WHERE (((((((NOT EXISTS (SELECT chs3.id FROM channels chs3 WHERE conversations.id = chs3.conversation_id AND chs3.user_id = 176)) AND (NOT EXISTS (SELECT chs2.id FROM channels chs2 WHERE conversations.id = chs2.conversation_id AND chs2.answer_request_response = 'answer'))) AND (NOT EXISTS (SELECT chs1.id FROM channels chs1 WHERE conversations.id = chs1.conversation_id AND chs1.flagged = 1))) AND (`conversations`.`canceled` = 0 )) AND (`conversations`.`error` = 0 )) AND (EXISTS (SELECT chs4.id FROM channels chs4 WHERE conversations.id = chs4.conversation_id AND chs4.asker = 1 AND chs4.has_question = 1))) AND (created_at > '2009-01-21 16:38:23'));
AR sticks it at the end of the query and as a result it runs in 6-10 seconds on acceptance (w/no query cache). Applying the created_at restriction at the end of the chain constructs a query with it at the beginning:
Conversation.with_question.
not_error.
not_canceled.
not_flagged.
not_answered.
not_involving(user).
in_the_last(1.day)
SELECT * FROM `conversations` WHERE (((((((created_at > '2009-01-21 18:07:29') AND (NOT EXISTS (SELECT chs3.id FROM channels chs3 WHERE conversations.id = chs3.conversation_id AND chs3.user_id = 176))) AND (NOT EXISTS (SELECT chs2.id FROM channels chs2 WHERE conversations.id = chs2.conversation_id AND chs2.answer_request_response = 'answer'))) AND (NOT EXISTS (SELECT chs1.id FROM channels chs1 WHERE conversations.id = chs1.conversation_id AND chs1.flagged = 1))) AND (`conversations`.`canceled` = 0 )) AND (`conversations`.`error` = 0 )) AND (EXISTS (SELECT chs4.id FROM channels chs4 WHERE conversations.id = chs4.conversation_id AND chs4.asker = 1 AND chs4.has_question = 1)))
Apparently it can now use the created_at to eliminate rows in the scan. The resulting query time is about 300ms on acceptance (again, w/no query cache).
Wednesday, January 21, 2009
Percona: How to Outrun The Lions
Presentation from Percona: How to Outrun The Lions
Great presentation on MySQL scaling.
http://www.scribd.com/doc/3929163/Percona-How-to-Outrun-The-Lions
Great presentation on MySQL scaling.
http://www.scribd.com/doc/3929163/Percona-How-to-Outrun-The-Lions
some really nice things about working at a startup...
...are that you don't have to worry too much about these questions:
http://blog.jayfields.com/2009/01/questions-to-ask-interviewer.html
--
Love,
Fritz
Tuesday, January 20, 2009
2 gems from SmugMug
I think smugmug is an organization worth emulating. They do what they do with 40 employees, and were far smaller for most of their existence. If you haven't yet, read about their deployment on ec2: simple, well-designed, and nearly fully-automated:
http://blogs.smugmug.com/don/2008/06/03/skynet-lives-aka-ec2-smugmug/
http://blogs.smugmug.com/don/2008/06/03/skynet-lives-aka-ec2-smugmug/
Also here are some pointers from them on mysql; the recommendation on Percona is something we should keep in mind should we need to call in some db expertise.
http://blogs.smugmug.com/don/2008/12/23/great-things-afoot-in-the-mysql-community/
--
Love,
Fritz
Monday, January 19, 2009
Anti-RDBMS: A list of distributed key-value stores
Richard Jones of Last.fm talks about his research and notes on distributed key-value stores.
Anti-RDBMS: A list of distributed key-value stores
Anti-RDBMS: A list of distributed key-value stores
Friday, January 16, 2009
Craigslist uses Sphinx
Well, I guess the cat's out of the bag! My first project at Craigslist was replacing MySQL FULLTEXT indexing with Sphinx. It wasn't the easiest road in the world, for a variety of reasons, but we got it all working and it's been humming along very well ever since.
I'm not going to go into a lot of details on the implementation here, other than to say Sphinx is faster and far more resource efficient than MySQL was for this task.
http://tinyurl.com/7gkwbz
Thursday, January 15, 2009
new in rails 2.2
http://guides.rubyonrails.org/2_2_release_notes.html
I definitely don't understand all the implications of threading or
connection pools. The automatic memoization is cool though.
--
Love,
Fritz
Building products users love by giving them control
A fun product design article from pcworld focused on consumer product designers and where they've gone right and wrong, emphasizing a theme of control that is somewhat relevant in our case.
http://www.pcworld.com/businesscenter/article/156305/memo_to_vendors_heres_how_to_build_a_winner.html
Finding unused indexes in mysql
Looks pretty easy to use:
http://hackmysql.com/mysqlidxchk
--
Love,
Fritz
Subscribe to:
Posts (Atom)