diff --git a/Dockerfile b/Dockerfile
index c8dd4cc45dfc8c482218aff9c9bfbc091bcebbc2..97dbe955141448dfec673910fcc815d178eed19d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -22,6 +22,7 @@ RUN cpanm \
     Mojo::Redis \
     Mojo::JWT \
     Mojolicious::Plugin::OpenAPI \
+    Mojolicious::Plugin::SentrySDK \
     Mojolicious::Plugin::SwaggerUI
 
 ADD . /opt/cf
diff --git a/lib/CF/Controller/Websockets.pm b/lib/CF/Controller/Websockets.pm
index 7c821e8d0d8f2c961094976045480d733eef49bc..91f5911f647b4d6b51b84a7593328f3734e45384 100644
--- a/lib/CF/Controller/Websockets.pm
+++ b/lib/CF/Controller/Websockets.pm
@@ -3,128 +3,71 @@ package CF::Controller::Websockets;
 use Mojo::Base 'Mojolicious::Controller';
 use Mojo::Pg::PubSub;
 use Digest::SHA qw(hmac_sha1_hex);
-use POSIX qw(ceil);
 use Encode qw(encode decode);
 
 use feature 'signatures';
 no warnings qw{ experimental::signatures };
 
 use constant SOCKET_INACTIVITY_TIMEOUT => 180;
+use constant ALIVE_TIME                => 60;
 
 sub main {
     my $c  = shift;
-    my $ip  = $c->tx->remote_address;
+    $c->inactivity_timeout(SOCKET_INACTIVITY_TIMEOUT);
+    $c->tx->with_compression;
+
     my $key = $c->req->headers->header('Sec-WebSocket-Key');
 
-    $c->inactivity_timeout(SOCKET_INACTIVITY_TIMEOUT);
+    my $pubsub = $c->redis->pubsub;
 
-    if ($c->req->headers->header('Sec-WebSocket-Extensions') =~ /permessage-deflate/) {
-        $c->tx->compressed(1);
-        $c->res->headers->add('Sec-WebSocket-Extensions' => 'permessage-deflate');
-    }
-
-    my $pubsub;
-
-    if ($c->cfg->{message_broker} eq 'redis') {
-        $pubsub = $c->redis->pubsub;
-        $pubsub->listen(notify => sub($pubsub, $payload) {
-            $c->send(decode("UTF-8", $payload));
-        });
-    }
-    else {
-        $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
-        $pubsub->listen(notify => sub($pubsub, $payload) {
-            $c->send($payload);
-        });
-    }
+    my $listener = $pubsub->listen(notify => sub($pubsub, $payload) {
+        $c->send(decode("UTF-8", $payload));
+    });
 
     $c->on(json => sub( $c, $message ) {
         if ( $message->{event} eq 'KEEPALIVE' ) {
 
             my $user;
+            my $is_member;
 
             if ($message->{payload} =~ /^\d+$/) {
-
-                $user = $c->pg->db->select('users',
-                    [qw(id username secret jitsi_allowed roles banned_until)],
-                    { id => $message->{payload}}
-                )->hash;
+                $user = $c->schema->resultset('User')->find({
+                    id => $message->{payload},
+                });
             }
 
             if ( $user ) {
-                my $sig = hmac_sha1_hex($message->{payload}, $user->{secret} );
+                my $sig = hmac_sha1_hex($message->{payload}, $user->secret );
                 if ( $sig ne $message->{sig} ) {
                     $c->app->log->warn(
-                        "Invalid signature for " . $user->{username}
+                        "Invalid signature for " . $user->username
                     );
-                    $user = undef;
                 }
-            }
-
-            eval {
-                my $tx = $c->pg->db->begin;
-
-                $c->pg->db->delete('sockets', [
-                    {id => $key},
-                    $user ? {user_id => $user->{id}}:{}
-                ]);
-
-                $c->pg->db->insert('sockets', {
-                    id        => $key,
-                    ip        => $ip,
-                    keepalive => \'now()',
-                    user_id   => $user ? $user->{id} : undef,
-                });
+                else {
+                    $user->update({ keepalive => \'now()' });
+                    $is_member = ( $user && $user->roles =~ /member/) ? 1:0;
 
-                $tx->commit;
-            };
+                    my $jitsi = $user->jitsi_allowed || $user->roles =~ /chairman|jitsi/;
 
-            my $all = $c->pg->db->query(
-                'select count(*) from sockets_view'
-            )->hash->{count};
-
-            my $members = $c->pg->db->query(
-                'select count(*) from sockets_view where is_member'
-            )->hash->{count};
-
-            my $group_size = $c->_member_group_size($members);
-
-            $c->send({json => { event => 'online_users_updated', payload => {
-                all             => $all,
-                members         => $members,
-                group_size_full => $group_size->{full},
-                group_size_half => $group_size->{half},
-            }}});
+                    $c->send({json => { event => 'user_status', payload => {
+                        jitsi_allowed => $jitsi ? \1:\0,
+                        is_banned     => $user->banned_until ? \1:\0,
+                    }}});
+                }
+            }
 
-            if ( $user ) {
-                my $jitsi = $user->{jitsi_allowed} || $user->{roles} =~ /chairman|jitsi/;
+            $c->redis->db->set(
+                join (':', ('live', 0, $is_member, $is_member ? $user->id : $key)), #TODO: event_id
+                'live', 'EX', ALIVE_TIME
+            );
 
-                $c->send({json => { event => 'user_status', payload => {
-                    jitsi_allowed => $jitsi ? \1:\0,
-                    is_banned     => $user->{banned_until} ? \1:\0,
-                }}});
-            }
         }
     });
 
     $c->on(finish => sub ($c, $code, $reason = undef) {
-        $pubsub->unlisten('notify');
+        $pubsub->unlisten('notify', $listener);
         $c->app->log->debug("WebSocket closed with status $code");
     });
 }
 
-sub _member_group_size  ($c, $total = 0){
-    my $group = 2 * sqrt($total);
-    my $min   = $total / 100;
-    my $max   = $total / 5;
-
-    $group = $min if $group < $min;
-    $group = $max if $group > $max;
-
-    return {
-        full => ceil( $group ),
-        half => ceil( $group/2 ),
-    };
-}
-
 1;
diff --git a/openapi.yaml b/openapi.yaml
index f32c79eb130fd35581379fd4cd8d968ad057cb80..b012eb586cdb8a29b7f9b73b79e0efd98b88f55e 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -1,7 +1,7 @@
 openapi: 3.0.3
 
 info:
-  version: "2.6.1"
+  version: "2.7.0"
   title: CF Online
   description: CF Online
   license:
diff --git a/script/online b/script/online
new file mode 100755
index 0000000000000000000000000000000000000000..6ec4ef29dd8244e19b4cf2fcd618293606f13c74
--- /dev/null
+++ b/script/online
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+
+use Mojo::IOLoop;
+use Mojo::Redis;
+use Mojo::JSON qw(encode_json);
+use POSIX qw(ceil);
+
+use feature 'signatures';
+no warnings qw{ experimental::signatures };
+
+my $tick  = 0;
+my $redis = Mojo::Redis->new($ENV{CFG_REDIS});
+my $id    = Mojo::IOLoop->recurring(1 => \&counter);
+
+Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
+
+sub counter {
+
+    my $counts = {};
+
+    $redis->db->keys( 'live:*', sub {
+        my ($db, $err, $res) = @_;
+
+        KEYS:
+        foreach my $key ( @{ $res} ) {
+            my (undef, $event_id, $role) = split /:/, $key;
+            $counts->{$event_id}{all}++;
+            $counts->{$event_id}{members}++ if $role;
+        }
+
+        EVENT:
+        foreach my $event_id ( keys %{ $counts } ) {
+            my $all = $counts->{$event_id}{all};
+
+            # zpomalovac
+            next EVENT if $tick > 9000/$all;
+
+            my $group_size = member_group_size($all); #TEST
+            $redis->pubsub->notify( notify => encode_json({
+                event   => 'online_users_updated',
+                payload => {
+                    all             => $all,
+                    members         => $counts->{$event_id}{members},
+#                   group_size_full => $group_size->{full},
+                    group_size_half => $group_size->{half},
+                    tick            => $tick,
+                }
+            }));
+        }
+    });
+
+    $tick = 0 if ++$tick > 9;
+}
+
+sub member_group_size  ($total = 0){
+    my $group = 2 * sqrt($total);
+    my $min   = $total / 100;
+    my $max   = $total / 5;
+
+    $group = $min if $group < $min;
+    $group = $max if $group > $max;
+
+    return {
+        full => ceil( $group ),
+        half => ceil( $group/2 ),
+    };
+}
+
+1;