Debuginfo

思考とアウトプット

RedisでNotification(通知)を実装する

弊社サービスTraBerryのWebアプリケーションで通知を実装しました。その実装方法の話です。

f:id:shoheik:20140929124437p:plain

Notificationで使ったRedis keyは2つ。Arrayで全体の通知の管理。Hashで中身の格納です。

f:id:shoheik:20140929121331j:plain

  • list:notification:user_id:<user_id>
  • hash:notification:user_id:<user_id>
  • 上記hashのキーは通知タイプによって違う。コンテンツは通知用のメッセージ。

list:notification:usr_id:<user_id>で0を新しい要素。数が多くなる程古い通知メッセージです。

具体例で見ていきます。

ユーザ1がPhoto1にlikeする。このとき、like:photo_id:1:<user_id>:has_read:0 というkeyを作る。 このkeyをlist:notification:user_id:<user_id>に追加。そして、hash:notification:user_id:<user_id>にそれをkey, valueを通知メッセージとして、hashに格納。

my $key = "notification:user_id:" . $notified_user_id;
my $field = "like:photo_id:" . $photo_id . ':user_id:' . $user_id . ':has_read:0' ; 

# Check notification length and then, delete and add 
my $notification_length = $self->cache->llen("list:$key");
$self->cache->rpop("list:$key") if ( $notification_length > 30);

$self->cache->lpush("list:$key", $field);
$self->cache->hset("hash:$key", $field => encode_json($message) }

新しい通知メッセージがあるかどうかは、配列の中のhas_read:0を探せば良い。 そして、通知が読まれたときには、has_read:1のkeyの入れ替えを行う。

sub get_notification {
    my ($self, $user_id) = @_;
    my @notification;
    my $index = 0;
    for my $field ( $self->cache->lrange("list:notification:user_id:${user_id}", 0, 30) ){
        my $message = $self->cache->hget("hash:notification:user_id:${user_id}", $field);
        my $has_read = 1;
        if( $field =~ /has_read\:0/){ # change has_read:0 to has_read:1 
            $self->cache->hdel("hash:notification:user_id:${user_id}", $field);
            $field =~ s/has_read\:0/has_read\:1/;
            $self->cache->lset("list:notification:user_id:${user_id}", $index, $field); 
            $self->cache->hset("hash:notification:user_id:${user_id}", $field, $message);
            $has_read = 0;
        }
        if (defined $message){
            my $m = decode_json( $message );
            $m->{has_read} = $has_read;
            push @notification, $m; 
        }
        $index++;
    }
    return \@notification;
}

通知を削除するときは、その要素を削除するだけ。