Como vocês resolveriam isto: sistema de pontuação

94 views
Skip to first unread message

Filipe

unread,
Aug 21, 2012, 6:24:45 PM8/21/12
to NodeJS Brasil
Pessoal, o cenário é o seguinte: Express, Socket.io e MongoDB.

Construí um sistema de chat onde o usuário é remunerado com pontos da
seguinte forma:
- Ganha 1 ponto a cada segundo que ele passa conectado
- Ganha 1 ponto a cada mensagem que ele envia ao chat
- Ganha 1 ponto, caso um outro usuário "curta" a mensagem dele.

Seja qual for a forma como ele ganha este ponto, o client side do
usuário precisa ter a resposta em tempo-real dos atuais pontos.

Como vocês resolveriam esta charada sem abarrotar o banco de dados? :)

(nao procuro código, apenas a lógica ou conceito)

diego nunes

unread,
Aug 21, 2012, 7:34:25 PM8/21/12
to nod...@googlegroups.com

    Você precisa do histórico de pontuação? Se não precisa, vai ter só  contador de pontos, provavelmente na tabela de usuários (ou no registro do usuário, se for um noSQL).
    Se precisar, sugiro uma tabela de registro da pontuação e um "cache" do somatório para ser consultado de forma rápida. A atualização desse cache seria em tempo real e haveria uma rotina de auto-manutenção para atualizar periodicamente o valor. Há muita coisa envolvida nesse tipo de técnica e não vou entrar em detalhes sem saber se é necessário porque a explicação seria longa e eu estou no celular :)

    Dê mais detalhes aí. A priori parece simples, na verdade.

Filipe

unread,
Aug 21, 2012, 7:52:51 PM8/21/12
to NodeJS Brasil
Diego, obrigado meu caro.

É noSQL (MongoDB).

Não é necessário o histórico, mas preciso que toda atualização na
pontuação do usuário (seja ela causada pelo próprio usuário, por um
usuário terceiro ou pelo segundo que se passou), precisa refletir no
lado do cliente.

Eu criei uma forma, mas ela pode explodir em pedaços o banco de dados:
a cada evento, eu faço um increment na pontuação atual do usuário em
questão (com uma atomic operation), pego então o valor atualizado
final e envio de volta ao usuário em questão.

Se estes eventos de atualização de pontos forem gerados somente nas
trocas de mensagens, ok (pois eu já estou salvando as mensagens no
banco). Mas e a atualização a cada segundo? Se eu tiver 300 pessoas
conectadas, são 300 interações com o banco de dados.

Ele vai explodir!



On Aug 21, 8:34 pm, diego nunes <dnu...@gmail.com> wrote:
>     Você precisa do histórico de pontuação? Se não precisa, vai ter só
> contador de pontos, provavelmente na tabela de usuários (ou no registro do
> usuário, se for um noSQL).
>     Se precisar, sugiro uma tabela de registro da pontuação e um "cache" do
> somatório para ser consultado de forma rápida. A atualização desse cache
> seria em tempo real e haveria uma rotina de auto-manutenção para atualizar
> periodicamente o valor. Há muita coisa envolvida nesse tipo de técnica e
> não vou entrar em detalhes sem saber se é necessário porque a explicação
> seria longa e eu estou no celular :)
>
>     Dê mais detalhes aí. A priori parece simples, na verdade.

diego nunes

unread,
Aug 21, 2012, 8:02:38 PM8/21/12
to nod...@googlegroups.com

    Agora fez sentido :)

   Obviamente você não vai enviar o evento de "passei um segundo no chat" para os usuários, somente os "joins" e os "parts". O resto é calculado no client.
    Como você estará usando socket, você já terá um evento de "saiu do chat" (dependendo do método de conexão, há um delay relativamente grande até a detecção de conexão fechada, então você vai ter que subtrair alguns segundos ou exigir determinados tipos de conexão).
    Assim somente os eventos de mensagem e de "like" vão ter broadcast (com a pontuação corrente e o timestamp, para atualizar o ponto de sincronia dos clients, como forma de auto-manutenção), e somente para os usuários "visíveis" (a sala atual, talvez?).
    Um bom serviço de socket suporta milhões de mensagens por segundo. O limite fica na conexão do client, não no socket. Considerando isso, para melhorar mais a performance, você pode fazer esses broadcasts periodicamente e não assim que os eventos ocorrem, ou enviar o broadcast num mesmo pacote que vai uma nova mensagem recebida no chat. Assim você teria as trocas de pacotes reduzidas a um mínimo enquanto mantém o ritmo de atualização super constante (dá pra manter os updates com menos de um segundo de atraso).

Filipe

unread,
Aug 21, 2012, 8:10:09 PM8/21/12
to NodeJS Brasil
Evento de auto manutenção... hmmm interessante.

O problema será que, se eu fizer o cálculo por tempo na desconexão do
chat, ao ter um evento externo que fará a manutenção dos pontos
correntes, eles não vão casar com o que está no lado do cliente, pois
nestes pontos correntes ainda não terá sido somado o tempo de conexão
(pelo disconnect).

Faz sentido?

On Aug 21, 9:02 pm, diego nunes <dnu...@gmail.com> wrote:
>     Agora fez sentido :)
>
>    Obviamente você não vai enviar o evento de "passei um segundo no chat"
> para os usuários, somente os "joins" e os "parts". O resto é calculado no
> client.
>     Como você estará usando socket, você já terá um evento de "saiu do
> chat" (dependendo do método de conexão, há um delay relativamente grande
> até a detecção de conexão fechada, então você vai ter que subtrair alguns
> segundos ou exigir determinados tipos de conexão).
>     Assim somente os eventos de mensagem e de "like" vão ter broadcast (com
> a pontuação corrente e o timestamp, para atualizar o ponto de sincronia dos
> clients, como forma de auto-manutenção), e somente para os usuários
> "visíveis" (a sala atual, talvez?).
>     Um bom serviço de socket suporta milhões de mensagens por segundo. O
> limite fica na conexão do client, não no socket. Considerando isso, para
> melhorar mais a performance, você pode fazer esses broadcasts
> periodicamente e não assim que os eventos ocorrem, ou enviar o broadcast
> num mesmo pacote que vai uma nova mensagem recebida no chat. Assim você
> teria as trocas de pacotes reduzidas a um mínimo enquanto mantém o ritmo de
> atualização super constante (dá pra manter os updates com menos de um
> segundo de atraso)*.*

@thwess

unread,
Aug 21, 2012, 8:39:25 PM8/21/12
to nod...@googlegroups.com
Eu acredito que seja assim como o Diego disse, vc mantém uma manutenção periódica e faz broadcasts somente para eventos não regulares.

Em relação ao disconnect, eu acredito que o socket consegue capturar o disconnect imediatamente, e já logo lançar o evento para o usuário. A questão mais preocupante é o disconnect por queda de conexão, que pode gerar aquele delay chato, e esse tempo ou vc corrige ou terá q dar mérito ao usuário q conseguiu manter o delay, pq c vc não conseguiu pegar o tempo exato que ele desconectou, então significa que os pontos até então computados, são dele e não devem ser removidos erroneamente.

Sobre o bd, eu aposto na mesma ficha da ideia de manutenção. Vc mantém toda pontuação dos usuários armazenada em uma collection no servidor e repassa para o bd em eventos regulares para diminuir o gargalo.

Bruno Carvalho

unread,
Aug 22, 2012, 12:12:20 AM8/22/12
to nod...@googlegroups.com
Sobre o lance dos segundos, a forma como eu resolveria inicialmente seria calcular os segundos quando ele saísse apenas. Ou seja, a pontuação por tempo ocorre: ao sair e a cada minuto. Vc registra a hora de entrada do indivíduo e uma vez por minuto faz broadcast da pontuação nova. Quando ele desconectar, voce pega a hora/min/seg da saida e diminui da hora/min/seg da entrada e soma os pontos no banco.

Assim, a pontuação por tempo é atualizada no banco de tempos em tempos, preferencialmente quando o usuário sai.

Tudo bem que seu modelo favorece "bots" e não sei se é isso que quer. Quem é antigo aqui e acessou a Efnet (IRC) sabe que um eggdrop bot ficava lá enquanto vc nao tava pra segurar o nick. Conexão desperdiçada sem ninguém de fato interagindo... ;)

Abs

Wemerson Couto Guimarães

unread,
Aug 22, 2012, 6:56:11 AM8/22/12
to nod...@googlegroups.com
Hum, como vai ter pontuacao, provavelmente tera nicknames fixos ou vai usar o ID do usuario... Que tal uma regra para medir inatividade depois de um determinado periodo... talvez usando um ou algo assim para reativar a contagem?

Tambem penso que se vai contar pontos por segundo, o usuario poderia ficar varias horas conectado mas sem interagir... essa contagem em vez de segundos ser por minutos como foi citado, a meu ver e bem mais interessante... pois voce estaria usando lado cliente para ajudar um pouco no processamento ao acumular a pontuacao a cada minuto e so entao enviar ao servidor...
--
Wemerson Guimarães
Rio Verde - Go - Brasil

Bruno Carvalho

unread,
Aug 22, 2012, 7:06:30 AM8/22/12
to nod...@googlegroups.com
O client enviar pontuação gera problemas de "cheating". Alguém vai descobrir e explorar (exploitar :P).

Essa pontuação por segundo é bastante viável, porém, como disse, dá margem pra "bots" existirem. Pontuação tem SEMPRE que ser calculada no server pra evitar cheating.

Abs

Wemerson Couto Guimarães

unread,
Aug 22, 2012, 4:16:18 PM8/22/12
to nod...@googlegroups.com
Realmente, não tinha me atentado pra isso... mas não é necessário computar cada segundo... e sim usar o horário de conexão/desconexao e computar o tempo ou uma seção com o tempo... e no próprio servidor fazer uma verificação periódica do tempo de inatividade para desconectar o usuário... senão fica o tempo todo conectado sem fazer nada... só ganhando pontos!

Filipe

unread,
Aug 22, 2012, 6:11:14 PM8/22/12
to NodeJS Brasil
Obrigado pelas sugestões pessoal! E incrível que foram exatamente
iguais as sugestões do pessoal lá da comunidade internacional do Node.
Muito legal!

Teve uma apenas que me chamou atenção por ser diferente. Basicamente a
idéia é criar um objeto com vários id e a manutenção desta pontuação
(seja get ou set) é feita contra eles.

E um processo paralelo se responsabiliza por salvar estas infos contra
o DB, no período ou momento que achar melhor.

O que acham?

On Aug 22, 5:16 pm, Wemerson Couto Guimarães <wemerso...@gmail.com>
wrote:
> Realmente, não tinha me atentado pra isso... mas não é necessário computar
> cada segundo... e sim usar o horário de conexão/desconexao e computar o
> tempo ou uma seção com o tempo... e no próprio servidor fazer uma
> verificação periódica do tempo de inatividade para desconectar o usuário...
> senão fica o tempo todo conectado sem fazer nada... só ganhando pontos!
>
> Em 22 de agosto de 2012 08:06, Bruno Carvalho <bment...@gmail.com> escreveu:
>
>
>
>
>
>
>
>
>
> > O client enviar pontuação gera problemas de "cheating". Alguém vai
> > descobrir e explorar (exploitar :P).
>
> > Essa pontuação por segundo é bastante viável, porém, como disse, dá margem
> > pra "bots" existirem. Pontuação tem SEMPRE que ser calculada no server pra
> > evitar cheating.
>
> > Abs
>
> > Em 22 de agosto de 2012 07:56, Wemerson Couto Guimarães <
> > wemerso...@gmail.com> escreveu:
>
> > Hum, como vai ter pontuacao, provavelmente tera nicknames fixos ou vai
> >> usar o ID do usuario... Que tal uma regra para medir inatividade depois de
> >> um determinado periodo... talvez usando um ou algo assim para reativar a
> >> contagem?
>
> >> Tambem penso que se vai contar pontos por segundo, o usuario poderia
> >> ficar varias horas conectado mas sem interagir... essa contagem em vez de
> >> segundos ser por minutos como foi citado, a meu ver e bem mais
> >> interessante... pois voce estaria usando lado cliente para ajudar um pouco
> >> no processamento ao acumular a pontuacao a cada minuto e so entao enviar ao
> >> servidor...
>
> >> Em 22 de agosto de 2012 01:12, Bruno Carvalho <bment...@gmail.com>escreveu:
>
> >> Sobre o lance dos segundos, a forma como eu resolveria inicialmente seria
> >>> calcular os segundos quando ele saísse apenas. Ou seja, a pontuação por
> >>> tempo ocorre: ao sair e a cada minuto. Vc registra a hora de entrada do
> >>> indivíduo e uma vez por minuto faz broadcast da pontuação nova. Quando ele
> >>> desconectar, voce pega a hora/min/seg da saida e diminui da hora/min/seg da
> >>> entrada e soma os pontos no banco.
>
> >>> Assim, a pontuação por tempo é atualizada no banco de tempos em tempos,
> >>> preferencialmente quando o usuário sai.
>
> >>> Tudo bem que seu modelo favorece "bots" e não sei se é isso que quer.
> >>> Quem é antigo aqui e acessou a Efnet (IRC) sabe que um eggdrop bot ficava
> >>> lá enquanto vc nao tava pra segurar o nick. Conexão desperdiçada sem
> >>> ninguém de fato interagindo... ;)
>
> >>> Abs
>

Ricardo Tomasi

unread,
Aug 22, 2012, 9:58:13 PM8/22/12
to nod...@googlegroups.com
Essa idéia do objeto na memória é válida se tu quiser compartilhar esses estados com outros usuários. Seria como usar o Redis pra parte da pontuação (muito mais rápido) e atualizar o perfil do usuário no mongo só na hora do disconnect.

Emerson Macedo

unread,
Aug 23, 2012, 9:23:30 AM8/23/12
to nod...@googlegroups.com
2012/8/21 Filipe <fil...@gmail.com>
Diego, obrigado meu caro.

É noSQL (MongoDB).

Não é necessário o histórico, mas preciso que toda atualização na
pontuação do usuário (seja ela causada pelo próprio usuário, por um
usuário terceiro ou pelo segundo que se passou), precisa refletir no
lado do cliente.

Eu criei uma forma, mas ela pode explodir em pedaços o banco de dados:
a cada evento, eu faço um increment na pontuação atual do usuário em
questão (com uma atomic operation), pego então o valor atualizado
final e envio de volta ao usuário em questão.

Se estes eventos de atualização de pontos forem gerados somente nas
trocas de mensagens, ok (pois eu já estou salvando as mensagens no
banco). Mas e a atualização a cada segundo? Se eu tiver 300 pessoas
conectadas, são 300 interações com o banco de dados.

Ele vai explodir!


Pra não explodir, usa o redis com o INCR e já faz broadcast em seguida. Depois você pode gravar em lotes no mongo.
 


On Aug 21, 8:34 pm, diego nunes <dnu...@gmail.com> wrote:
>     Você precisa do histórico de pontuação? Se não precisa, vai ter só
> contador de pontos, provavelmente na tabela de usuários (ou no registro do
> usuário, se for um noSQL).
>     Se precisar, sugiro uma tabela de registro da pontuação e um "cache" do
> somatório para ser consultado de forma rápida. A atualização desse cache
> seria em tempo real e haveria uma rotina de auto-manutenção para atualizar
> periodicamente o valor. Há muita coisa envolvida nesse tipo de técnica e
> não vou entrar em detalhes sem saber se é necessário porque a explicação
> seria longa e eu estou no celular :)
>
>     Dê mais detalhes aí. A priori parece simples, na verdade.
> On Aug 21, 2012 7:24 PM, "Filipe" <fili...@gmail.com> wrote:
>
>
>
>
>
>
>
> > Pessoal, o cenário é o seguinte: Express, Socket.io e MongoDB.
>
> > Construí um sistema de chat onde o usuário é remunerado com pontos da
> > seguinte forma:
> >  - Ganha 1 ponto a cada segundo que ele passa conectado
> >  - Ganha 1 ponto a cada mensagem que ele envia ao chat
> >  - Ganha 1 ponto, caso um outro usuário "curta" a mensagem dele.
>
> > Seja qual for a forma como ele ganha este ponto, o client side do
> > usuário precisa ter a resposta em tempo-real dos atuais pontos.
>
> > Como vocês resolveriam esta charada sem abarrotar o banco de dados? :)
>
> > (nao procuro código, apenas a lógica ou conceito)



--
Emerson Macedo
http://codificando.com

Filipe

unread,
Aug 23, 2012, 6:32:23 PM8/23/12
to NodeJS Brasil
Interessante! Eu sou completamente noob em Node e ainda não sei o que
é Redis, vou estudar a respeito.

Muito obrigado!

On Aug 23, 10:23 am, Emerson Macedo <emerle...@gmail.com> wrote:
> 2012/8/21 Filipe <fili...@gmail.com>

Delcio Torres

unread,
Aug 22, 2012, 7:55:08 AM8/22/12
to nod...@googlegroups.com

Ja pensou em REDIS e Broadcast / Client ?

http://www.youtube.com/watch?v=qnuuQpNZScw

Filipe

unread,
Aug 29, 2012, 10:29:15 PM8/29/12
to NodeJS Brasil
Delcio, tudo bem meu caro?

Estou conseguindo resolver com as soluções propostas aqui e está
ficando muito legal. Eu ainda não pisei no campo do REDIS, mas com
certeza será meu próximo upgrade de conhecimento.

O objeto na memória está funcionando muito bem e é MUITO rápido. E
para me prevenir de perder estes dados (já que não uso o REDIS como
sugerido), eu criei uma rotina que de, em tempo em tempo, ele salva
todos os objetos da memória no banco de dados. Está sendo um belo
estudo, quando terminar publico aqui os resultados e compartilho como
fiz caso alguém se interesse :)

On Aug 22, 8:55 am, Delcio Torres <delciotor...@gmail.com> wrote:
> Ja pensou em REDIS e Broadcast / Client ?
>
> http://www.youtube.com/watch?v=qnuuQpNZScw
> Em 22/08/2012 08:06, "Bruno Carvalho" <bment...@gmail.com> escreveu:
>
>
>
>
>
>
>
> > O client enviar pontuação gera problemas de "cheating". Alguém vai
> > descobrir e explorar (exploitar :P).
>
> > Essa pontuação por segundo é bastante viável, porém, como disse, dá margem
> > pra "bots" existirem. Pontuação tem SEMPRE que ser calculada no server pra
> > evitar cheating.
>
> > Abs
>
> > Em 22 de agosto de 2012 07:56, Wemerson Couto Guimarães <
> > wemerso...@gmail.com> escreveu:
>
> >> Hum, como vai ter pontuacao, provavelmente tera nicknames fixos ou vai
> >> usar o ID do usuario... Que tal uma regra para medir inatividade depois de
> >> um determinado periodo... talvez usando um ou algo assim para reativar a
> >> contagem?
>
> >> Tambem penso que se vai contar pontos por segundo, o usuario poderia
> >> ficar varias horas conectado mas sem interagir... essa contagem em vez de
> >> segundos ser por minutos como foi citado, a meu ver e bem mais
> >> interessante... pois voce estaria usando lado cliente para ajudar um pouco
> >> no processamento ao acumular a pontuacao a cada minuto e so entao enviar ao
> >> servidor...
>
> >> Em 22 de agosto de 2012 01:12, Bruno Carvalho <bment...@gmail.com>escreveu:
>
> >> Sobre o lance dos segundos, a forma como eu resolveria inicialmente seria
> >>> calcular os segundos quando ele saísse apenas. Ou seja, a pontuação por
> >>> tempo ocorre: ao sair e a cada minuto. Vc registra a hora de entrada do
> >>> indivíduo e uma vez por minuto faz broadcast da pontuação nova. Quando ele
> >>> desconectar, voce pega a hora/min/seg da saida e diminui da hora/min/seg da
> >>> entrada e soma os pontos no banco.
>
> >>> Assim, a pontuação por tempo é atualizada no banco de tempos em tempos,
> >>> preferencialmente quando o usuário sai.
>
> >>> Tudo bem que seu modelo favorece "bots" e não sei se é isso que quer.
> >>> Quem é antigo aqui e acessou a Efnet (IRC) sabe que um eggdrop bot ficava
> >>> lá enquanto vc nao tava pra segurar o nick. Conexão desperdiçada sem
> >>> ninguém de fato interagindo... ;)
>
> >>> Abs
>

Kaique da silva

unread,
Aug 30, 2012, 12:29:57 AM8/30/12
to nod...@googlegroups.com

Mongo, redis, zero mq, socket io e session.

Reply all
Reply to author
Forward
0 new messages