With all the fuzz around chrome and web sockets I’ve bundled up together a simple web sockets implementation for twisted. It’s a really easy way to interact with browsers. In the next days I will run benchmarks, because I think it will stir up a bit the webserver scene a bit, and possibly, the assynchronous [...]
In this article I’ll talk about reasons to use mongoDB and other no-sql databases.
Relational databases are often used in many web apps. But usually when the time comes to scale the app to a few millions users, you have to make choices on your architecture.
For instance, a common practice to handle high load is to put one or more of the most used tables in separate servers. In order to use this technique, a developer will have to resolve the issue of join queries. How do you query tables that aren’t in the same location and yet have high performance?
One solution is to denormalize data, thus duplicating content in all tables that may have a query that needs to know about it. Think of 2 tables: contacts and users. The tables are separated into 2 different servers. And you have a feature in your app, in which you show all contacts from a specific user. You need a join query, but you can’t use it. So you duplicate some of the fields from the contacts table, right inside your users table. For instance, you create a field called contact_names, in which you put only the names, of all contacts from that user, separated by commas. It’s a easy way to solve the problem, but it comes with a cost. You have to worry about syncing the contacts in all tables that know something about contacts.
Bottom line? You started developing your app with join queries, but at some point you had to give up on it.
So, if using a traditional database forces you to stop using some of its features somewhere down the road, why not start with a kind of database that avoids the things that are not scalable and sustainable in the long run?
In mongoDB a solution for this problem would be creating a document Contacts, and embed it inside the document Users. So, each user will have its contacts right there, inside each one of the User records. No need to use join queries.
However, there are times when you need to have a model that is connected to several others.
For example, let’s say you need to relate Contacts to several models such as Clients, Suppliers and Employees. So you create 4 collections: Clients, Suppliers, Employees and Contacts. You connect them all together via a db reference. This acts like a foreign key. But, this is not the mongoDB way to do things. Performance will penalized.
So the general question should always be “Why can’t I embed this document?“. Or even better: “Does this object merit its own collection, or rather should it embed in objects in other collections?“.
There are some general rules on when to embed, and when to reference (grabbed from mongodb website):
“First class” objects, that are at top level, typically have their own collection;
Line item detail objects typically are embedded;
Objects which follow an object modelling “contains” relationship should generally be embedded;
Many to many relationships are generally by reference;
Collections with only a few objects may safely exist as separate collections, as the whole collection is quickly cached in application server memory;
Embedded objects are harder to reference than “top level” objects in collections, as you cannot have a DBRef to an embedded object (at least not yet);
It is more difficult to get a system-level view for embedded objects. For example, it would be easier to query the top 100 scores across all students if Scores were not embedded;
If the amount of data to embed is huge (many megabytes), you may reach the limit on size of a single object;
If performance is an issue, embed;
The way I see it, you can still have more or less the best of both worlds: the flexibility of documents and the performance of embedded documents. And you still have a way to emulate foreign keys, like a relational database - but not without a penalty on performance. I don’t know how mongoDB and MySQL compare to each other in the long run, for the usual web app. It’d be cool if someone did some benchmarks on this subject.
Domingo, dia 20 de dezembro de 2009, corri a Etapa de Verão do Circuito das Estações Adidas.
O percurso de 10 Km foi todo nas ruas da região do Pacaembu, largando em frente ao estádio, passando pela avenida Pacaembu, Elevado Costa e Silva e terminando com a chegada também em frente ao portão principal do estádio [...]
It’s been a long time I hear about mongoDB and its awesomeness, so now’s the time to play with it!
In this article I’ll try to summarize what’s mongoDB, why it’s so cool, and how to start playing with it.
What is mongoDB?
mongoDB is one of the new NO-SQL databases. It means it’s not record-oriented like relational databases. Instead, it’s schema-free and collection oriented. And also, it’s a document database.
So what’s a document? It’s the first unit of data in a mongoDB. A document is simply an array of key-value pairs like this:
{
name: "John Doe",
age: 40
}
Note: we call this notation BSON, which stands for “Binary Serialized dOcument Notation”.
Back to the document. This “document” is similar to a record in a usual relational database. You can use Strings, Integers and many other data types, including arrays and other Documents. You have the ability to nest Documents, like, Person -> Children -> Toys.
So what’s a table then? Collections act more or like tables. A collection holds one or more documents.
And ultimately a database is a group of collections. Each collection has a unique name inside a database.
With the document stuff out of the way, let’s see why this database seems to cool.
Why is mongoDB so cool?
Working with a no-SQL database means you have several advantages over a traditional database. Here are some of them:
there is no schema. You don’t have to use Rails migrations for creating tables and columns. You simply start using them, and the database creates them on the fly. When using the gem mongomapper, for example, you simply declare the keys inside your model, and that’s it. Simple like that;
the data is formatted using a JSON-like format, giving greater flexibility and at the same time simplicity. New data types can be added, depending on how your format your Document before saving it into the database;
storage of binary files such as videos and photos on the database is possible and more important, efficient;
Basically, mongoDB bridges the gap between key-value stores (which are highly scalable) and traditional RDBMS (which provide structured schemas and powerful queries).
Other than these things, mongoDB also supports some types of database replication. It also offers auto-sharding, a feature that allows one to build a large horizontally scalable database cluster that can incorporate additional machines dinamically. It also supports map-reduce.
Installing mongoDB
Simply download the binaries. Unpack the tar.gz to /usr/local/bin/mongo and add ‘/usr/local/bin/mongo/bin’ to your PATH. Create the directory to which the database files will be saved to:
mkdir -p /data/db
Start the server with:
mongod
It listens for connections on port 27017 by default. Open another shell and start the mongo shell with:
mongo
Interesting enough, the mongo shell uses Javascript as its language
No need to create a database
One important note. You never have to create a database or collection.
The moment you try to access a database or collection, the underlying database and/or collection is created automatically.
When we ran db.things.find(); in the last example, the shell automatically showed all data from the collection. But if we assign a variable to the find() method, we can iterate the data it as we wish:
> var cursor = db.things.find();
> cursor[5]
{ "_id" : ObjectId("4b33df5b844fab562308bb64"), "x" : 4, "j" : 4 }
Important note: mongoDB cursors are not snapshots. For instance, if your cursor has 10 documents, and another user removes one of them from the collection, your cursor will return only 9 documents. You have to use explicit locking to prevent this.
How to query data
Let’s see how to query the database for specific things we need to find.
The important thing to know is that queries, in a mongoDB database, are documents themselves. Let’s have a look:
Now, if we want to specify what fields we need, instead of running a “SELECT *” query, mongoDB lets you return “partial documents”. To do this, you supply a second argument to the find() method, specifying what elements you need it to return:
Note that in this case, all fields will be returned.
Conclusion
mongoDB is easy to learn. Its features are useful for web apps, specially having the need to build something scalable right from the beginning. Being a document database, it provides greater flexibility never before offered by traditional databases. mongoDB and its friends like CouchDB are definitely worth a look.
In the next article we’ll see how to start playing with mongoDB in Rails. Stay tuned!
Explorar a versão mais recente de um framework ou linguagem é prática comum entre programadores que têm paixão pela sua arte. E este simples gosto pela experimentação exige que tenhamos uma maneira prática e rápida para alternar entre o bleeding edge e outras versões que utilizamos nos nossos projetos. Vou mostrar aqui uma maneira de manter várias versões do Ruby no Snow Leopard (MacOS 10.6.2). A mesma abordagem funciona para o Leopard (MacOS 10.5.8).
O Snow Leopard já vem com o Ruby 1.8.7 instalado em /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr. O meu objetivo é poder alternar facilmente entre o Ruby 1.8.7 e o Ruby 1.9.1. Para tanto, me falta a versão mais nova do Ruby:
Resumidamente, baixamos, descompactamos e configuramos o Ruby 1.9 pra ser instalado na pasta /usr/local/ruby-1.9.1. Para efetivamento instalá-lo na pasta configurada, basta fazer o seguinte:
A execução do comando abaixo indica que já temos o Ruby 1.9.1 instalado e funcionando:
O próximo passo é destruir os links para o Ruby que o Snow Leopard guarda em /usr/bin/
Agora, precisamos criar um link em /usr/local
E depois colocar o nosso novo link no path:
E para alternar facilmente entre as versões existentes, costumo criar aliases que redirecionam o link /usr/local/ruby para a versão que eu desejo utilizar
O resultado:
Esta é somente a forma com que eu lido com o problema de ter mais de uma versão de uma linguagem no meu ambiente de trabalho. Podem existir outras, mas gosto da praticidade e do controle que ganho com esta abordagem.
Recurrence é uma biblioteca criada para gerar eventos recorrentes de maneira simples. Eu criei essa gem há tempos, mas nunca escrevi nada sobre ela. Eis que muitas pessoas começaram a me mandar e-mails perguntando como utilizá-la e chegou a hora de fazer um artigo mostrando seu uso na prática.
A primeira coisa que você precisa fazer é instalar a gem.
sudo gem install recurrence
Depois, basta adicionar à biblioteca.
require"rubygems"require"recurrence"
A maneira mais simples de usar é instanciar um objeto através dos métodos de classe daily, weekly, monthly e yearly, que permitem criar eventos diários, semanais, mensais e anuais, respectivamente.
r = Recurrence.daily
Para retornar a próxima data, você deve utilizar o método Recurrence#next.
r.next#=> Fri, 18 Dec 2009
r.next#=> Fri, 18 Dec 2009
Você também pode utilizar o método Recurrence#next!, que altera a variável interna de data, como mostra o exemplo abaixo.
r.next!
#=> Fri, 18 Dec 2009
r.next!
#=> Sat, 19 Dec 2009
Se você quiser, pode especificar o intervalo de dias que o evento pode se repetir; basta utilizar a opção :starts e :ends.
# define the starting date to 2009-12-24
r = Recurrence.daily(:starts => Date.new(2009, 12, 24))# define the ending date to 2009-12-31
r = Recurrence.daily(:ends => Date.new(2009, 12, 31))
Você também pode passar essas datas como strings, desde que elas sejam interpretadas pelo método Date#parse.
r = Recurrence.daily(:starts => "2009-12-24")
Os atalhos são uma excelente maneira de tornar o código mais semântico, mas em alguns casos é melhor seguir com o bom e velho método new.
Como você pode ver no exemplo acima, estamos criando um evento semanal, que acontece aos sábados, a cada duas semanas. Ao utilizar o método Recurrence#next! você terá algo como isso:
r.next!
#=> Sat, 19 Dec 2009
r.next!
#=> Sat, 02 Jan 2010
Você também pode especificar uma lista com os dias da semana que o evento deve ocorrer.
r = Recurrence.weekly(:on => [:saturday, :sunday])
r.next!
#=> Sat, 19 Dec 2009
r.next!
#=> Sun, 20 Dec 2009
r.next!
#=> Sat, 26 Dec 2009
Para acessar uma lista com todos os eventos disponíveis, basta utilizar o método Recurrence#events.
r.events#=> [Sun, 20 Dec 2009, Sun, 27 Dec 2009]
Esse método fará irá guardar a lista de datas geradas, aumentando a performance se ele for acessado mais de uma vez. Se você quiser regerar essas datas, pode utilizar o método Recurrence#events!.
Também é possível verificar se uma data está no intervalo especificado no objeto Recurrence.
r = Recurrence.weekly(:on => :saturday)
r.include? "2009-12-19"#=> true
Como usar na prática
Como você pode ver, o Recurrence é totalmente parametrizado. Essa abordagem permite que você armazene os itens necessários para montar sua recorrência em um banco de dados, por exemplo. Digamos que você tenha um modelo Event, com o seguinte schema:
Os atributos que serão utilizados no Recurrence são :meta e :scheduled_to. O primeiro irá armazenar o tipo de recorrência e as informações como dia, intervalo, etc. Já o segundo, irá armazenar a próxima data do evento, para diminuir a geração de datas. O modelo Event pode armazenar os atributos como este exemplo:
Na hora de montar o objeto de recorrência, você pode utilizar algo como isto:
event = Event.first
r = Recurrence.new(event.meta.merge(:starts => event.scheduled_to))
r.next#=> Tue, 05 Jan 2010
Se você precisar exibir um evento mais de uma vez, utilize este mesmo processo e exiba os eventos com o método events.
Finalizando
Criar eventos recorrentes nem sempre é uma tarefa simples, principalmente no que diz respeito à performance. O José Valim fez uma série de contribuições que ajudaram a melhor esse ponto, além de outras funcionalidades como os atalhos.
Para conhecer mais sobre as opções disponíveis do Recurrence, acesse o README do projeto no Github.
Em um post anterior mostrei como serializar objetos em JSON utilizando .NET. Agora vamos fazer a mesma coisa com Ruby on Rails.
Vamos utilizar como exemplo uma classe de modelo chamada SomeFake:
class SomeFake < ActiveRecord::Base
end
Utilizando essa migration:
class CreateSomeFakes < ActiveRecord::Migration
def self.up
create_table :some_fakes do |t|
t.string [...]
O RSpec é um framework bastante completo e, por isso mesmo, muitas coisas são desconhecidas por grande parte dos desenvolvedores. Neste artigo, você conhecerá 7 coisas que irão mudar a maneira como você utiliza o RSpec.
Subject
O RSpec possui um método muito útil chamado subject, que retorna uma instância da classe que está sendo utilizada como contexto do exemplo.
describe User do
it { should_not be_admin }end
A grande vantagem desta abordagem é que você aumenta consideravelmente a legibilidade de suas especificações. Mas você não precisa utilizar apenas o contexto especificado no método describe; se quiser, você pode definir o seu próprio!
describe User do
subject { User.new(:admin => true)}
it { should be_admin }end
Quando outros tipos de objetos (como módulos ou strings) são passados ao método describe, ele irá retornar o próprio objeto.
Message expectation
O método stub_chain
Sempre que você precisar fazer uma chamada encadeada e quiser verificar o último valor, utilize o método stub_chain. Ele permite transformar um bloco como este
describe "Mocks"do
before do@user = mock()@things = mock()@recent = mock()@user.stub!(:things).and_return(@things)@things.stub!(:recent).and_return(@recent)@recent.should_receive(:count).and_return(100)end
it "should return the recent things count from an user"do@user.things.recent.count.should == 100endend
em uma coisa bem mais simples como esta
describe "Mocks"do
before do@user = mock()@user.stub_chain(:things, :recent, :count).and_return(100)end
it "should return the recent things count from an user"do@user.things.recent.count.should == 100endend
O método and_return
Você alguma vez precisou acessar um stub mais de uma vez e queria que ele retornasse diferentes valores em cada chamada? Veja este exemplo.
require"open-uri"class Dice
API_URL = "http://www.random.org/integers/?num=1&min=1&max=6&col=1&base=10&format=plain&rnd=new"def roll
open(API_URL).to_iendend
Toda vez que o método roll for chamado, ele irá fazer uma requisição diferente ao http://random.org. Imagine que por algum motivo você precisasse retornar diferentes valores toda vez que o método open você invocado. Tudo o que você precisa fazer é retornar mais de um valor com o método and_return.
describe Dice do
before do
subject.should_receive(:open).and_return("1", "4", "2")end
it "should not cache request"do
subject.roll.should == 1
subject.roll.should == 4
subject.roll.should == 2endend
O método with
O RSpec disponibiliza uma série de métodos que podem ser usados em conjunto com o método with. Veja alguns exemplos:
module Echo
extend selfdef echo(*args)
args.inspectendend
describe Echo do# with any kind of argument
specify("anything"){
subject.should_receive(:echo).with(anything)
subject.echo(1)}# with hash containing values
specify("hash_including"){
subject.should_receive(:echo).with(hash_including(:say => "hello"))
subject.echo(:say => "hello")}# with an instance of String
specify("instance_of"){
subject.should_receive(:echo).with(instance_of(String))
subject.echo("hello")}# with an object that responds to some methods
specify("duck_type"){
subject.should_receive(:echo).twice.with(duck_type(:<<))
subject.echo(Array.new)
subject.echo(String.new)}end
Muitas vezes precisamos preparar nosso ambiente antes de executarmos nossos testes. Quando isso precisa ser feito em mais de uma especificação, você pode diminuir a duplicação de código utilizando blocos globais.
No seu arquivo spec_helper.rb, você pode utilizar os métodos append_before, append_after, prepend_before e prepend_after.
Em vez de comentar temporariamente exemplos que você não quer que sejam executados, você pode utilizar o método pending. O método pending pode ser utilizado nos blocos de before. Assim, todos os exemplos daquele contexto de describe serão automaticamente marcados como pendente.
describe "Pending examples"do
before do
pending
end# lots of examplesend
Você também pode deixar apenas um exemplo pendente.
describe "Pending examples"do
it "should be pending"do
pending "need to work on it"
Env.setup!
endend
Ou apenas uma parte dele.
describe "Pending examples"do
it "should be pending with a block"do
pending("need to working on it as well")do
Env.setup!
endendend
Uma outra alternativa (que não é deixar como pendente mas sim desabilitado) é utilizar o método xit em vez de it.
describe "Pending examples"do
xit "should be pending with alias"do
Env.setup!
endend
Matchers personalizados
Sempre que você perceber que está repetindo um padrão na hora de escrever seus exemplos, automatize o processo criando novos matchers. O RSpec possui duas maneiras diferentes de fazer isso. A mais simples delas é utilizando o método simple_matcher. Crie um módulo chamado TheAnswerMatchers para adicionarmos novos matchers.
module TheAnswerMatchers
def be_the_answer
simple_matcher do |given, matcher|
# save some typing
the_answer = "the Answer to Life, the Universe, and Everything"
matcher.description = "be #{the_answer}"
matcher.failure_message = "expected #{given.inspect} to be #{the_answer}"
matcher.negative_failure_message = "expected #{given.inspect} not to be #{the_answer}"
given == 42endendend
Agora, basta fazer com que o RSpec reconheça esses novos matchers; é só incluir o módulo.
Spec::Runner.configuredo |config|
config.include TheAnswerMatchers
end
E para escrever seus exemplos, você pode usar o matcher be_the_answer:
describe "The Answer to Life, the Universe, and Everything"do
specify {42.should be_the_answer }
specify {"the wrong answer".should_not be_the_answer }end
Finalizando…
Como você pode perceber, é bem simples escrever exemplos melhores no RSpec. Se você olhar as specs do RSpec, encontrará bastante coisa legal que você normalmente não veria (porque tem muita coisa para ler) no RDocs.
Este blog está parcialmente de volta. Acabei de migrá-lo para os servidores da Locaweb, empresa para qual estou trabalhando desde novembro, embora ainda não tenha restaurado os meus antigos posts (e nem sei se vou). Mas, enfim, estou de volta
Eu sou um curioso com relação à soluções de datacenter, não apenas pela questão ambiental mas também pela parte tecnológica. Sempre que posso, visito datacenters. No Brasil conheci vários, não apenas em São Paulo, mas no Rio e em Brasília, inclusive o de órgãos do governo e estatais. Nos EUA eu conheci o datacenter do Citibank (longa história), mas isso foi há alguns anos, de maneira que a “nave-mãe” que vi na época deve ter mudado bastante desde então.
Tudo isso para dizer que faz tempo que não via um desenho e solução tão interessante para datacenter como esta. Este datacenter em Quebéc oferece um pouco dos dois mundos (preocupação ambiental e eficiência técnica) através da reutilização de estruturas existentes, aproveitamento de “resíduos” (ar quente para calefação do escritório adjacente), economia de material de cabeamento, facilidade de manutenção/gestão e refrigeração, não apenas com um modelo mais inteligente/inovador que o atual “corredor quente/corredor frio”, mas com a possibilidade de aproveitamento do ar gelado externo (no inverno), entre outras soluções simples, mas inteligentes. Vale a pena conhecer (via Datacenter Knowledge).