diff --git a/lib/CF/Controller/Websockets.pm b/lib/CF/Controller/Websockets.pm
index cbabd5d7ac13fdcde995d08990f251e393f20ad2..bb8afc10d09bbe1cc57545566105458b5094fe1a 100644
--- a/lib/CF/Controller/Websockets.pm
+++ b/lib/CF/Controller/Websockets.pm
@@ -16,35 +16,39 @@ sub main {
     $c->inactivity_timeout(SOCKET_INACTIVITY_TIMEOUT);
     $c->tx->with_compression;
 
-    my $key = $c->req->headers->header('Sec-WebSocket-Key');
-
+    my $key    = $c->req->headers->header('Sec-WebSocket-Key');
     my $pubsub = $c->redis->pubsub;
+    my $user;
+    my $event_id = 0; #TODO - parametr!
 
-    my $listener = $pubsub->listen(notify => sub($pubsub, $payload) {
-        $c->send(decode("UTF-8", $payload));
-    });
-
-    my $listener_online = $pubsub->listen('online:0' => sub($pubsub, $payload) { #TODO: event_id
-        my @counts = split ' ', $payload;
-        $c->send({json => {
-            event   => 'online_users_updated',
-            payload => {
-                all             => $counts[0]+0,
-                members         => $counts[1]+0,
-                group_size_full => $counts[2]+0,
-                group_size_half => $counts[3]+0,
-            }
-        }});
-    });
+    my $listener = $pubsub->listen(
+        "notify:$event_id" => sub($pubsub, $payload) {
+            $c->send(decode("UTF-8", $payload));
+        }
+    );
+
+    my $listener_online = $pubsub->listen(
+        "online:$event_id" => sub($pubsub, $payload) {
+            my @counts = split ' ', $payload;
+            $c->send({json => {
+                event   => 'online_users_updated',
+                payload => {
+                    all             => $counts[0]+0,
+                    members         => $counts[1]+0,
+                    group_size_full => $counts[2]+0,
+                    group_size_half => $counts[3]+0,
+                }
+            }});
+        }
+    );
 
     $c->on(json => sub( $c, $message ) {
         if ( $message->{event} eq 'KEEPALIVE' ) {
 
-            my $user;
             my $is_member = 0;
 
             if ($message->{payload} =~ /^\d+$/) {
-                $user = $c->schema->resultset('User')->find({
+                $user ||= $c->schema->resultset('User')->find({
                     id => $message->{payload},
                 });
             }
@@ -57,7 +61,7 @@ sub main {
                     );
                 }
                 else {
-                    $user->update({ keepalive => \'now()' });
+                    $user->add_to_alive({ event_id => $event_id });
                     $is_member = 1 if $user && $user->roles =~ /member/;
 
                     my $jitsi = $user->jitsi_allowed || $user->roles =~ /chairman|jitsi/;
@@ -70,7 +74,7 @@ sub main {
             }
 
             $c->redis->db->set(
-                join (':', ('live', 0, $is_member, $is_member ? $user->id : $key)), #TODO: event_id
+                join (':', ('live', $event_id, $is_member, $is_member ? $user->id : $key)),
                 'live', 'EX', ALIVE_TIME
             );
 
@@ -78,8 +82,8 @@ sub main {
     });
 
     $c->on(finish => sub ($c, $code, $reason = undef) {
-        $pubsub->unlisten('notify', $listener);
-        $pubsub->unlisten('online:0', $listener_online); #TODO: event_id
+        $pubsub->unlisten("notify:$event_id", $listener);
+        $pubsub->unlisten("online:$event_id", $listener_online);
         $c->app->log->debug("WebSocket closed with status $code");
     });
 }
diff --git a/lib/CF/Helpers/Core.pm b/lib/CF/Helpers/Core.pm
index 999e8f51b92d9d7338582e83ec55ba17ab4dc2ea..46c05f1d7426d370c5992a78358f1163c4f0ced0 100644
--- a/lib/CF/Helpers/Core.pm
+++ b/lib/CF/Helpers/Core.pm
@@ -201,7 +201,7 @@ sub register ($class, $self, $conf) {
 
     });
 
-    $self->helper( notify => sub ( $c, $event, $payload ) {
+    $self->helper( "notify:0" => sub ( $c, $event, $payload ) { #TODO: event_id
 
         my $content = {
             event   => $event,
diff --git a/lib/CF/Schema/Result/Alive.pm b/lib/CF/Schema/Result/Alive.pm
new file mode 100644
index 0000000000000000000000000000000000000000..94dba03ef30fb6959e6c96692778d6db54dc4f8a
--- /dev/null
+++ b/lib/CF/Schema/Result/Alive.pm
@@ -0,0 +1,44 @@
+package CF::Schema::Result::Alive;
+
+use strict;
+use warnings;
+use feature 'signatures';
+no warnings qw{ experimental::signatures };
+
+use base 'DBIx::Class::Core';
+
+our $VERSION = 1;
+
+__PACKAGE__->table('alive');
+
+__PACKAGE__->add_columns(
+    id => {
+        data_type         => 'integer',
+        is_auto_increment => 1,
+        is_nullable       => 0,
+        sequence          => 'uid_seq'
+    },
+    qw(
+        event_id
+        user_id
+        timestamp
+    ),
+);
+
+__PACKAGE__->set_primary_key('id');
+
+__PACKAGE__->belongs_to(
+    event => 'CF::Schema::Result::Event',
+    {
+        'foreign.id' => 'self.user_id',
+    },
+);
+
+__PACKAGE__->belongs_to(
+    user => 'CF::Schema::Result::User',
+    {
+        'foreign.id' => 'self.user_id',
+    },
+);
+
+1;
diff --git a/lib/CF/Schema/Result/User.pm b/lib/CF/Schema/Result/User.pm
index aa043cdc911553f1b3a08ff30035eda1c66aafee..9fea904aa376a6e1b7e81f851eead5b77b784289 100644
--- a/lib/CF/Schema/Result/User.pm
+++ b/lib/CF/Schema/Result/User.pm
@@ -28,7 +28,6 @@ __PACKAGE__->add_columns(
         name
         roles
         main_group_name
-        keepalive
         banned_until
         jitsi_allowed
     ),
@@ -45,6 +44,11 @@ __PACKAGE__->has_many(
     { 'foreign.user_id' => 'self.id', },
 );
 
+__PACKAGE__->has_many(
+    alive => 'CF::Schema::Result::Alive',
+    { 'foreign.user_id' => 'self.id', },
+);
+
 sub formatted($self) {
 
     my $user = {
diff --git a/sql/11/up.sql b/sql/11/up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..dc9b49755a330dfc15476666f97e9be49da5bcc1
--- /dev/null
+++ b/sql/11/up.sql
@@ -0,0 +1,11 @@
+create table "alive" (
+    "id" integer not null default nextval('uid_seq'),
+    "event_id" integer not null,
+    "user_id" integer not null,
+    "timestamp" timestamp(0) not null default now(),
+    foreign key ("event_id") references "events" ("id") on update cascade on delete cascade,
+    foreign key ("user_id") references "users" ("id") on update cascade on delete cascade
+);
+create index "alive_timestamp_idx" on "alive" ("timestamp");
+
+alter table "users" drop column "keepalive";